4711 lines · 164.7 KB · Rust
RawDownload
// Copyright 2021 The Jujutsu Authors//// Licensed under the Apache License, Version 2.0 (the "License");// you may not use this file except in compliance with the License.// You may obtain a copy of the License at//// https://www.apache.org/licenses/LICENSE-2.0//// Unless required by applicable law or agreed to in writing, software// distributed under the License is distributed on an "AS IS" BASIS,// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.// See the License for the specific language governing permissions and// limitations under the License. #![allow(missing_docs)] use std::collections::{HashMap, HashSet};use std::convert::Infallible;use std::ops::Range;use std::path::Path;use std::rc::Rc;use std::str::FromStr;use std::sync::Arc;use std::{error, fmt, iter}; use itertools::Itertools;use once_cell::sync::Lazy;use pest::iterators::{Pair, Pairs};use pest::pratt_parser::{Assoc, Op, PrattParser};use pest::Parser;use pest_derive::Parser;use thiserror::Error; use crate::backend::{BackendError, BackendResult, ChangeId, CommitId};use crate::commit::Commit;use crate::git;use crate::hex_util::to_forward_hex;use crate::object_id::{HexPrefix, PrefixResolution};use crate::op_store::WorkspaceId;use crate::repo::Repo;use crate::repo_path::{FsPathParseError, RepoPathBuf};use crate::revset_graph::RevsetGraphEdge;use crate::store::Store;use crate::str_util::StringPattern; /// Error occurred during symbol resolution.#[derive(Debug, Error)]pub enum RevsetResolutionError {    #[error("Revision \"{name}\" doesn't exist")]    NoSuchRevision {        name: String,        candidates: Vec<String>,    },    #[error("Workspace \"{name}\" doesn't have a working copy")]    WorkspaceMissingWorkingCopy { name: String },    #[error("An empty string is not a valid revision")]    EmptyString,    #[error("Commit ID prefix \"{0}\" is ambiguous")]    AmbiguousCommitIdPrefix(String),    #[error("Change ID prefix \"{0}\" is ambiguous")]    AmbiguousChangeIdPrefix(String),    #[error("Unexpected error from store")]    StoreError(#[source] BackendError),} /// Error occurred during revset evaluation.#[derive(Debug, Error)]pub enum RevsetEvaluationError {    #[error("Unexpected error from store")]    StoreError(#[source] BackendError),    #[error("{0}")]    Other(String),} #[derive(Parser)]#[grammar = "revset.pest"]pub struct RevsetParser; impl Rule {    /// Whether this is a placeholder rule for compatibility with the other    /// systems.    fn is_compat(&self) -> bool {        matches!(            self,            Rule::compat_parents_op                | Rule::compat_dag_range_op                | Rule::compat_dag_range_pre_op                | Rule::compat_dag_range_post_op                | Rule::compat_add_op                | Rule::compat_sub_op        )    }     fn to_symbol(self) -> Option<&'static str> {        match self {            Rule::EOI => None,            Rule::identifier_part => None,            Rule::identifier => None,            Rule::symbol => None,            Rule::literal_string => None,            Rule::whitespace => None,            Rule::at_op => Some("@"),            Rule::pattern_kind_op => Some(":"),            Rule::parents_op => Some("-"),            Rule::children_op => Some("+"),            Rule::compat_parents_op => Some("^"),            Rule::dag_range_op            | Rule::dag_range_pre_op            | Rule::dag_range_post_op            | Rule::dag_range_all_op => Some("::"),            Rule::compat_dag_range_op            | Rule::compat_dag_range_pre_op            | Rule::compat_dag_range_post_op => Some(":"),            Rule::range_op => Some(".."),            Rule::range_pre_op | Rule::range_post_op | Rule::range_all_op => Some(".."),            Rule::range_ops => None,            Rule::range_pre_ops => None,            Rule::range_post_ops => None,            Rule::range_all_ops => None,            Rule::negate_op => Some("~"),            Rule::union_op => Some("|"),            Rule::intersection_op => Some("&"),            Rule::difference_op => Some("~"),            Rule::compat_add_op => Some("+"),            Rule::compat_sub_op => Some("-"),            Rule::infix_op => None,            Rule::function_name => None,            Rule::keyword_argument => None,            Rule::argument => None,            Rule::function_arguments => None,            Rule::formal_parameters => None,            Rule::string_pattern => None,            Rule::primary => None,            Rule::neighbors_expression => None,            Rule::range_expression => None,            Rule::expression => None,            Rule::program => None,            Rule::alias_declaration_part => None,            Rule::alias_declaration => None,        }    }} #[derive(Debug)]pub struct RevsetParseError {    kind: RevsetParseErrorKind,    pest_error: Option<Box<pest::error::Error<Rule>>>,    origin: Option<Box<RevsetParseError>>,} #[derive(Debug, Error, PartialEq, Eq)]pub enum RevsetParseErrorKind {    #[error("Syntax error")]    SyntaxError,    #[error("'{op}' is not a prefix operator")]    NotPrefixOperator {        op: String,        similar_op: String,        description: String,    },    #[error("'{op}' is not a postfix operator")]    NotPostfixOperator {        op: String,        similar_op: String,        description: String,    },    #[error("'{op}' is not an infix operator")]    NotInfixOperator {        op: String,        similar_op: String,        description: String,    },    #[error(r#"Revset function "{name}" doesn't exist"#)]    NoSuchFunction {        name: String,        candidates: Vec<String>,    },    #[error("Invalid arguments to revset function \"{name}\": {message}")]    InvalidFunctionArguments { name: String, message: String },    #[error("Invalid file pattern")]    FsPathParseError(#[source] FsPathParseError),    #[error("Cannot resolve file pattern without workspace")]    FsPathWithoutWorkspace,    #[error(r#"Cannot resolve "@" without workspace"#)]    WorkingCopyWithoutWorkspace,    #[error("Redefinition of function parameter")]    RedefinedFunctionParameter,    #[error(r#"Alias "{0}" cannot be expanded"#)]    BadAliasExpansion(String),    #[error(r#"Alias "{0}" expanded recursively"#)]    RecursiveAlias(String),} impl RevsetParseError {    fn new(kind: RevsetParseErrorKind) -> Self {        RevsetParseError {            kind,            pest_error: None,            origin: None,        }    }     fn with_span(kind: RevsetParseErrorKind, span: pest::Span<'_>) -> Self {        let message = iter::successors(Some(&kind as &dyn error::Error), |e| e.source()).join(": ");        let err = pest::error::Error::new_from_span(            pest::error::ErrorVariant::CustomError { message },            span,        );        RevsetParseError {            kind,            pest_error: Some(Box::new(err)),            origin: None,        }    }     fn with_span_and_origin(        kind: RevsetParseErrorKind,        span: pest::Span<'_>,        origin: Self,    ) -> Self {        let message = iter::successors(Some(&kind as &dyn error::Error), |e| e.source()).join(": ");        let err = pest::error::Error::new_from_span(            pest::error::ErrorVariant::CustomError { message },            span,        );        RevsetParseError {            kind,            pest_error: Some(Box::new(err)),            origin: Some(Box::new(origin)),        }    }     pub fn kind(&self) -> &RevsetParseErrorKind {        &self.kind    }     /// Original parsing error which typically occurred in an alias expression.    pub fn origin(&self) -> Option<&Self> {        self.origin.as_deref()    }} impl From<pest::error::Error<Rule>> for RevsetParseError {    fn from(err: pest::error::Error<Rule>) -> Self {        RevsetParseError {            kind: RevsetParseErrorKind::SyntaxError,            pest_error: Some(Box::new(rename_rules_in_pest_error(err))),            origin: None,        }    }} impl fmt::Display for RevsetParseError {    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {        if let Some(err) = &self.pest_error {            err.fmt(f)        } else {            self.kind.fmt(f)        }    }} impl error::Error for RevsetParseError {    fn source(&self) -> Option<&(dyn error::Error + 'static)> {        if let Some(e) = self.origin() {            return Some(e as &dyn error::Error);        }        match &self.kind {            // SyntaxError is a wrapper for pest::error::Error.            RevsetParseErrorKind::SyntaxError => {                self.pest_error.as_ref().map(|e| e as &dyn error::Error)            }            // Otherwise the kind represents this error.            e => e.source(),        }    }} fn rename_rules_in_pest_error(mut err: pest::error::Error<Rule>) -> pest::error::Error<Rule> {    let pest::error::ErrorVariant::ParsingError {        positives,        negatives,    } = &mut err.variant    else {        return err;    };     // Remove duplicated symbols. Compat symbols are also removed from the    // (positive) suggestion.    let mut known_syms = HashSet::new();    positives.retain(|rule| {        !rule.is_compat() && rule.to_symbol().map_or(true, |sym| known_syms.insert(sym))    });    let mut known_syms = HashSet::new();    negatives.retain(|rule| rule.to_symbol().map_or(true, |sym| known_syms.insert(sym)));    err.renamed_rules(|rule| {        rule.to_symbol()            .map(|sym| format!("`{sym}`"))            .unwrap_or_else(|| format!("<{rule:?}>"))    })} // assumes index has less than u64::MAX entries.pub const GENERATION_RANGE_FULL: Range<u64> = 0..u64::MAX;pub const GENERATION_RANGE_EMPTY: Range<u64> = 0..0; /// Symbol or function to be resolved to `CommitId`s.#[derive(Clone, Debug, Eq, PartialEq)]pub enum RevsetCommitRef {    WorkingCopy(WorkspaceId),    Symbol(String),    RemoteSymbol {        name: String,        remote: String,    },    VisibleHeads,    Root,    Branches(StringPattern),    RemoteBranches {        branch_pattern: StringPattern,        remote_pattern: StringPattern,    },    Tags,    GitRefs,    GitHead,} #[derive(Clone, Debug, Eq, PartialEq)]pub enum RevsetFilterPredicate {    /// Commits with number of parents in the range.    ParentCount(Range<u32>),    /// Commits with description containing the needle.    Description(StringPattern),    /// Commits with author's name or email containing the needle.    Author(StringPattern),    /// Commits with committer's name or email containing the needle.    Committer(StringPattern),    /// Commits modifying the paths specified by the pattern.    File(Option<Vec<RepoPathBuf>>), // TODO: embed matcher expression?    /// Commits with conflicts    HasConflict,} #[derive(Debug, PartialEq, Eq, Clone)]pub enum RevsetExpression {    None,    All,    Commits(Vec<CommitId>),    CommitRef(RevsetCommitRef),    // TODO: This shouldn't be of RevsetExpression type. Maybe better to    // introduce an intermediate AST tree where aliases will be substituted.    StringPattern {        kind: String,        value: String,    },    Ancestors {        heads: Rc<RevsetExpression>,        generation: Range<u64>,    },    Descendants {        roots: Rc<RevsetExpression>,        generation: Range<u64>,    },    // Commits that are ancestors of "heads" but not ancestors of "roots"    Range {        roots: Rc<RevsetExpression>,        heads: Rc<RevsetExpression>,        generation: Range<u64>,    },    // Commits that are descendants of "roots" and ancestors of "heads"    DagRange {        roots: Rc<RevsetExpression>,        heads: Rc<RevsetExpression>,        // TODO: maybe add generation_from_roots/heads?    },    Heads(Rc<RevsetExpression>),    Roots(Rc<RevsetExpression>),    Latest {        candidates: Rc<RevsetExpression>,        count: usize,    },    Filter(RevsetFilterPredicate),    /// Marker for subtree that should be intersected as filter.    AsFilter(Rc<RevsetExpression>),    Present(Rc<RevsetExpression>),    NotIn(Rc<RevsetExpression>),    Union(Rc<RevsetExpression>, Rc<RevsetExpression>),    Intersection(Rc<RevsetExpression>, Rc<RevsetExpression>),    Difference(Rc<RevsetExpression>, Rc<RevsetExpression>),} impl RevsetExpression {    pub fn none() -> Rc<RevsetExpression> {        Rc::new(RevsetExpression::None)    }     pub fn all() -> Rc<RevsetExpression> {        Rc::new(RevsetExpression::All)    }     pub fn working_copy(workspace_id: WorkspaceId) -> Rc<RevsetExpression> {        Rc::new(RevsetExpression::CommitRef(RevsetCommitRef::WorkingCopy(            workspace_id,        )))    }     pub fn symbol(value: String) -> Rc<RevsetExpression> {        Rc::new(RevsetExpression::CommitRef(RevsetCommitRef::Symbol(value)))    }     pub fn remote_symbol(name: String, remote: String) -> Rc<RevsetExpression> {        let commit_ref = RevsetCommitRef::RemoteSymbol { name, remote };        Rc::new(RevsetExpression::CommitRef(commit_ref))    }     pub fn commit(commit_id: CommitId) -> Rc<RevsetExpression> {        RevsetExpression::commits(vec![commit_id])    }     pub fn commits(commit_ids: Vec<CommitId>) -> Rc<RevsetExpression> {        Rc::new(RevsetExpression::Commits(commit_ids))    }     pub fn visible_heads() -> Rc<RevsetExpression> {        Rc::new(RevsetExpression::CommitRef(RevsetCommitRef::VisibleHeads))    }     pub fn root() -> Rc<RevsetExpression> {        Rc::new(RevsetExpression::CommitRef(RevsetCommitRef::Root))    }     pub fn branches(pattern: StringPattern) -> Rc<RevsetExpression> {        Rc::new(RevsetExpression::CommitRef(RevsetCommitRef::Branches(            pattern,        )))    }     pub fn remote_branches(        branch_pattern: StringPattern,        remote_pattern: StringPattern,    ) -> Rc<RevsetExpression> {        Rc::new(RevsetExpression::CommitRef(            RevsetCommitRef::RemoteBranches {                branch_pattern,                remote_pattern,            },        ))    }     pub fn tags() -> Rc<RevsetExpression> {        Rc::new(RevsetExpression::CommitRef(RevsetCommitRef::Tags))    }     pub fn git_refs() -> Rc<RevsetExpression> {        Rc::new(RevsetExpression::CommitRef(RevsetCommitRef::GitRefs))    }     pub fn git_head() -> Rc<RevsetExpression> {        Rc::new(RevsetExpression::CommitRef(RevsetCommitRef::GitHead))    }     pub fn latest(self: &Rc<RevsetExpression>, count: usize) -> Rc<RevsetExpression> {        Rc::new(RevsetExpression::Latest {            candidates: self.clone(),            count,        })    }     pub fn filter(predicate: RevsetFilterPredicate) -> Rc<RevsetExpression> {        Rc::new(RevsetExpression::Filter(predicate))    }     /// Commits in `self` that don't have descendants in `self`.    pub fn heads(self: &Rc<RevsetExpression>) -> Rc<RevsetExpression> {        Rc::new(RevsetExpression::Heads(self.clone()))    }     /// Commits in `self` that don't have ancestors in `self`.    pub fn roots(self: &Rc<RevsetExpression>) -> Rc<RevsetExpression> {        Rc::new(RevsetExpression::Roots(self.clone()))    }     /// Parents of `self`.    pub fn parents(self: &Rc<RevsetExpression>) -> Rc<RevsetExpression> {        Rc::new(RevsetExpression::Ancestors {            heads: self.clone(),            generation: 1..2,        })    }     /// Ancestors of `self`, including `self`.    pub fn ancestors(self: &Rc<RevsetExpression>) -> Rc<RevsetExpression> {        self.ancestors_range(GENERATION_RANGE_FULL)    }     /// Ancestors of `self`, including `self` until `generation` back.    pub fn ancestors_at(self: &Rc<RevsetExpression>, generation: u64) -> Rc<RevsetExpression> {        self.ancestors_range(generation..(generation + 1))    } 

Lines 1–500 of 4711.