mirror of
https://github.com/martinvonz/jj.git
synced 2025-02-01 00:50:57 +00:00
revset: disable parsing rules of legacy dag range operator
The legacy parsing rules are turned into compatibility errors. The x:y rule is temporarily enabled when parsing string patterns. It's weird, but we can't isolate the parsing function because a string pattern may be defined in an alias.
This commit is contained in:
parent
2905a70b18
commit
815437598f
7 changed files with 92 additions and 97 deletions
|
@ -22,6 +22,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||||
* Dropped support for the "legacy" graph-drawing style. Use "ascii" for a very
|
* Dropped support for the "legacy" graph-drawing style. Use "ascii" for a very
|
||||||
similar result.
|
similar result.
|
||||||
|
|
||||||
|
* Dropped support for the deprecated `:` revset operator. Use `::` instead.
|
||||||
|
|
||||||
### New features
|
### New features
|
||||||
|
|
||||||
* Templates now support logical operators: `||`, `&&`, `!`
|
* Templates now support logical operators: `||`, `&&`, `!`
|
||||||
|
|
|
@ -408,7 +408,12 @@ impl From<RevsetParseError> for CommandError {
|
||||||
let message = iter::successors(Some(&err), |e| e.origin()).join("\n");
|
let message = iter::successors(Some(&err), |e| e.origin()).join("\n");
|
||||||
// Only for the top-level error as we can't attach hint to inner errors
|
// Only for the top-level error as we can't attach hint to inner errors
|
||||||
let hint = match err.kind() {
|
let hint = match err.kind() {
|
||||||
RevsetParseErrorKind::NotPostfixOperator {
|
RevsetParseErrorKind::NotPrefixOperator {
|
||||||
|
op: _,
|
||||||
|
similar_op,
|
||||||
|
description,
|
||||||
|
}
|
||||||
|
| RevsetParseErrorKind::NotPostfixOperator {
|
||||||
op: _,
|
op: _,
|
||||||
similar_op,
|
similar_op,
|
||||||
description,
|
description,
|
||||||
|
|
|
@ -28,53 +28,6 @@ fn test_log_with_empty_revision() {
|
||||||
"###);
|
"###);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_log_legacy_range_operator() {
|
|
||||||
let test_env = TestEnvironment::default();
|
|
||||||
test_env.jj_cmd_ok(test_env.env_root(), &["init", "repo", "--git"]);
|
|
||||||
let repo_path = test_env.env_root().join("repo");
|
|
||||||
|
|
||||||
let (stdout, stderr) = test_env.jj_cmd_ok(&repo_path, &["log", "-r=@:"]);
|
|
||||||
insta::assert_snapshot!(stdout, @r###"
|
|
||||||
@ qpvuntsm test.user@example.com 2001-02-03 04:05:07.000 +07:00 230dd059
|
|
||||||
│ (empty) (no description set)
|
|
||||||
~
|
|
||||||
"###);
|
|
||||||
insta::assert_snapshot!(stderr, @r###"
|
|
||||||
The `:` revset operator is deprecated. Please switch to `::`.
|
|
||||||
"###);
|
|
||||||
let (stdout, stderr) = test_env.jj_cmd_ok(&repo_path, &["log", "-r=:@"]);
|
|
||||||
insta::assert_snapshot!(stdout, @r###"
|
|
||||||
@ qpvuntsm test.user@example.com 2001-02-03 04:05:07.000 +07:00 230dd059
|
|
||||||
│ (empty) (no description set)
|
|
||||||
◉ zzzzzzzz root() 00000000
|
|
||||||
"###);
|
|
||||||
insta::assert_snapshot!(stderr, @r###"
|
|
||||||
The `:` revset operator is deprecated. Please switch to `::`.
|
|
||||||
"###);
|
|
||||||
let (stdout, stderr) = test_env.jj_cmd_ok(&repo_path, &["log", "-r=root():@"]);
|
|
||||||
insta::assert_snapshot!(stdout, @r###"
|
|
||||||
@ qpvuntsm test.user@example.com 2001-02-03 04:05:07.000 +07:00 230dd059
|
|
||||||
│ (empty) (no description set)
|
|
||||||
◉ zzzzzzzz root() 00000000
|
|
||||||
"###);
|
|
||||||
insta::assert_snapshot!(stderr, @r###"
|
|
||||||
The `:` revset operator is deprecated. Please switch to `::`.
|
|
||||||
"###);
|
|
||||||
let (stdout, stderr) = test_env.jj_cmd_ok(
|
|
||||||
&repo_path,
|
|
||||||
&["log", "-r=x", "--config-toml", "revset-aliases.x = '@:'"],
|
|
||||||
);
|
|
||||||
insta::assert_snapshot!(stdout, @r###"
|
|
||||||
@ qpvuntsm test.user@example.com 2001-02-03 04:05:07.000 +07:00 230dd059
|
|
||||||
│ (empty) (no description set)
|
|
||||||
~
|
|
||||||
"###);
|
|
||||||
insta::assert_snapshot!(stderr, @r###"
|
|
||||||
The `:` revset operator is deprecated. Please switch to `::`.
|
|
||||||
"###);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_log_with_or_without_diff() {
|
fn test_log_with_or_without_diff() {
|
||||||
let test_env = TestEnvironment::default();
|
let test_env = TestEnvironment::default();
|
||||||
|
|
|
@ -20,6 +20,17 @@ fn test_syntax_error() {
|
||||||
test_env.jj_cmd_ok(test_env.env_root(), &["init", "repo", "--git"]);
|
test_env.jj_cmd_ok(test_env.env_root(), &["init", "repo", "--git"]);
|
||||||
let repo_path = test_env.env_root().join("repo");
|
let repo_path = test_env.env_root().join("repo");
|
||||||
|
|
||||||
|
let stderr = test_env.jj_cmd_failure(&repo_path, &["log", "-r", ":x"]);
|
||||||
|
insta::assert_snapshot!(stderr, @r###"
|
||||||
|
Error: Failed to parse revset: --> 1:1
|
||||||
|
|
|
||||||
|
1 | :x
|
||||||
|
| ^
|
||||||
|
|
|
||||||
|
= ':' is not a prefix operator
|
||||||
|
Hint: Did you mean '::' for ancestors?
|
||||||
|
"###);
|
||||||
|
|
||||||
let stderr = test_env.jj_cmd_failure(&repo_path, &["log", "-r", "x &"]);
|
let stderr = test_env.jj_cmd_failure(&repo_path, &["log", "-r", "x &"]);
|
||||||
insta::assert_snapshot!(stderr, @r###"
|
insta::assert_snapshot!(stderr, @r###"
|
||||||
Error: Failed to parse revset: --> 1:4
|
Error: Failed to parse revset: --> 1:4
|
||||||
|
|
|
@ -59,8 +59,6 @@ only symbols.
|
||||||
* `x::y`: Descendants of `x` that are also ancestors of `y`. Equivalent
|
* `x::y`: Descendants of `x` that are also ancestors of `y`. Equivalent
|
||||||
to `x:: & ::y`. This is what `git log` calls `--ancestry-path x..y`.
|
to `x:: & ::y`. This is what `git log` calls `--ancestry-path x..y`.
|
||||||
* `::`: All visible commits in the repo. Equivalent to `all()`.
|
* `::`: All visible commits in the repo. Equivalent to `all()`.
|
||||||
* `:x`, `x:`, and `x:y`: Deprecated versions of `::x`, `x::`, and `x::y` We
|
|
||||||
plan to delete them in jj 0.15+.
|
|
||||||
* `x..y`: Ancestors of `y` that are not also ancestors of `x`. Equivalent to
|
* `x..y`: Ancestors of `y` that are not also ancestors of `x`. Equivalent to
|
||||||
`::y ~ ::x`. This is what `git log` calls `x..y` (i.e. the same as we call it).
|
`::y ~ ::x`. This is what `git log` calls `x..y` (i.e. the same as we call it).
|
||||||
* `..x`: Ancestors of `x`, including the commits in `x` itself, but excluding
|
* `..x`: Ancestors of `x`, including the commits in `x` itself, but excluding
|
||||||
|
|
|
@ -34,17 +34,16 @@ dag_range_op = { "::" }
|
||||||
dag_range_pre_op = { "::" }
|
dag_range_pre_op = { "::" }
|
||||||
dag_range_post_op = { "::" }
|
dag_range_post_op = { "::" }
|
||||||
dag_range_all_op = { "::" }
|
dag_range_all_op = { "::" }
|
||||||
// TODO: Drop support for these in 0.15+
|
compat_dag_range_op = { ":" }
|
||||||
legacy_dag_range_op = { ":" }
|
compat_dag_range_pre_op = { ":" }
|
||||||
legacy_dag_range_pre_op = { ":" }
|
compat_dag_range_post_op = { ":" }
|
||||||
legacy_dag_range_post_op = { ":" }
|
|
||||||
range_op = { ".." }
|
range_op = { ".." }
|
||||||
range_pre_op = { ".." }
|
range_pre_op = { ".." }
|
||||||
range_post_op = { ".." }
|
range_post_op = { ".." }
|
||||||
range_all_op = { ".." }
|
range_all_op = { ".." }
|
||||||
range_ops = _{ dag_range_op | legacy_dag_range_op | range_op }
|
range_ops = _{ dag_range_op | compat_dag_range_op | range_op }
|
||||||
range_pre_ops = _{ dag_range_pre_op | legacy_dag_range_pre_op | range_pre_op }
|
range_pre_ops = _{ dag_range_pre_op | compat_dag_range_pre_op | range_pre_op }
|
||||||
range_post_ops = _{ dag_range_post_op | legacy_dag_range_post_op | range_post_op }
|
range_post_ops = _{ dag_range_post_op | compat_dag_range_post_op | range_post_op }
|
||||||
range_all_ops = _{ dag_range_all_op | range_all_op }
|
range_all_ops = _{ dag_range_all_op | range_all_op }
|
||||||
|
|
||||||
negate_op = { "~" }
|
negate_op = { "~" }
|
||||||
|
|
|
@ -82,17 +82,12 @@ impl Rule {
|
||||||
fn is_compat(&self) -> bool {
|
fn is_compat(&self) -> bool {
|
||||||
matches!(
|
matches!(
|
||||||
self,
|
self,
|
||||||
Rule::compat_parents_op | Rule::compat_add_op | Rule::compat_sub_op
|
Rule::compat_parents_op
|
||||||
)
|
| Rule::compat_dag_range_op
|
||||||
}
|
| Rule::compat_dag_range_pre_op
|
||||||
|
| Rule::compat_dag_range_post_op
|
||||||
/// Whether this is a deprecated rule to be removed in later version.
|
| Rule::compat_add_op
|
||||||
fn is_legacy(&self) -> bool {
|
| Rule::compat_sub_op
|
||||||
matches!(
|
|
||||||
self,
|
|
||||||
Rule::legacy_dag_range_op
|
|
||||||
| Rule::legacy_dag_range_pre_op
|
|
||||||
| Rule::legacy_dag_range_post_op
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -112,9 +107,9 @@ impl Rule {
|
||||||
| Rule::dag_range_pre_op
|
| Rule::dag_range_pre_op
|
||||||
| Rule::dag_range_post_op
|
| Rule::dag_range_post_op
|
||||||
| Rule::dag_range_all_op => Some("::"),
|
| Rule::dag_range_all_op => Some("::"),
|
||||||
Rule::legacy_dag_range_op
|
Rule::compat_dag_range_op
|
||||||
| Rule::legacy_dag_range_pre_op
|
| Rule::compat_dag_range_pre_op
|
||||||
| Rule::legacy_dag_range_post_op => Some(":"),
|
| Rule::compat_dag_range_post_op => Some(":"),
|
||||||
Rule::range_op => Some(".."),
|
Rule::range_op => Some(".."),
|
||||||
Rule::range_pre_op | Rule::range_post_op | Rule::range_all_op => Some(".."),
|
Rule::range_pre_op | Rule::range_post_op | Rule::range_all_op => Some(".."),
|
||||||
Rule::range_ops => None,
|
Rule::range_ops => None,
|
||||||
|
@ -155,6 +150,12 @@ pub struct RevsetParseError {
|
||||||
pub enum RevsetParseErrorKind {
|
pub enum RevsetParseErrorKind {
|
||||||
#[error("Syntax error")]
|
#[error("Syntax error")]
|
||||||
SyntaxError,
|
SyntaxError,
|
||||||
|
#[error("'{op}' is not a prefix operator")]
|
||||||
|
NotPrefixOperator {
|
||||||
|
op: String,
|
||||||
|
similar_op: String,
|
||||||
|
description: String,
|
||||||
|
},
|
||||||
#[error("'{op}' is not a postfix operator")]
|
#[error("'{op}' is not a postfix operator")]
|
||||||
NotPostfixOperator {
|
NotPostfixOperator {
|
||||||
op: String,
|
op: String,
|
||||||
|
@ -282,13 +283,11 @@ fn rename_rules_in_pest_error(mut err: pest::error::Error<Rule>) -> pest::error:
|
||||||
return err;
|
return err;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Remove duplicated symbols. Legacy or compat symbols are also removed from the
|
// Remove duplicated symbols. Compat symbols are also removed from the
|
||||||
// (positive) suggestion.
|
// (positive) suggestion.
|
||||||
let mut known_syms = HashSet::new();
|
let mut known_syms = HashSet::new();
|
||||||
positives.retain(|rule| {
|
positives.retain(|rule| {
|
||||||
!rule.is_compat()
|
!rule.is_compat() && rule.to_symbol().map_or(true, |sym| known_syms.insert(sym))
|
||||||
&& !rule.is_legacy()
|
|
||||||
&& rule.to_symbol().map_or(true, |sym| known_syms.insert(sym))
|
|
||||||
});
|
});
|
||||||
let mut known_syms = HashSet::new();
|
let mut known_syms = HashSet::new();
|
||||||
negatives.retain(|rule| rule.to_symbol().map_or(true, |sym| known_syms.insert(sym)));
|
negatives.retain(|rule| rule.to_symbol().map_or(true, |sym| known_syms.insert(sym)));
|
||||||
|
@ -489,13 +488,6 @@ impl RevsetExpression {
|
||||||
pub fn ancestors(self: &Rc<RevsetExpression>) -> Rc<RevsetExpression> {
|
pub fn ancestors(self: &Rc<RevsetExpression>) -> Rc<RevsetExpression> {
|
||||||
self.ancestors_range(GENERATION_RANGE_FULL)
|
self.ancestors_range(GENERATION_RANGE_FULL)
|
||||||
}
|
}
|
||||||
fn legacy_ancestors(self: &Rc<RevsetExpression>) -> Rc<RevsetExpression> {
|
|
||||||
Rc::new(RevsetExpression::Ancestors {
|
|
||||||
heads: self.clone(),
|
|
||||||
generation: GENERATION_RANGE_FULL,
|
|
||||||
is_legacy: true,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Ancestors of `self`, including `self` until `generation` back.
|
/// Ancestors of `self`, including `self` until `generation` back.
|
||||||
pub fn ancestors_at(self: &Rc<RevsetExpression>, generation: u64) -> Rc<RevsetExpression> {
|
pub fn ancestors_at(self: &Rc<RevsetExpression>, generation: u64) -> Rc<RevsetExpression> {
|
||||||
|
@ -531,13 +523,6 @@ impl RevsetExpression {
|
||||||
is_legacy: false,
|
is_legacy: false,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
fn legacy_descendants(self: &Rc<RevsetExpression>) -> Rc<RevsetExpression> {
|
|
||||||
Rc::new(RevsetExpression::Descendants {
|
|
||||||
roots: self.clone(),
|
|
||||||
generation: GENERATION_RANGE_FULL,
|
|
||||||
is_legacy: true,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Descendants of `self`, including `self` until `generation` ahead.
|
/// Descendants of `self`, including `self` until `generation` ahead.
|
||||||
pub fn descendants_at(self: &Rc<RevsetExpression>, generation: u64) -> Rc<RevsetExpression> {
|
pub fn descendants_at(self: &Rc<RevsetExpression>, generation: u64) -> Rc<RevsetExpression> {
|
||||||
|
@ -834,6 +819,8 @@ struct ParseState<'a> {
|
||||||
locals: &'a HashMap<&'a str, Rc<RevsetExpression>>,
|
locals: &'a HashMap<&'a str, Rc<RevsetExpression>>,
|
||||||
user_email: &'a str,
|
user_email: &'a str,
|
||||||
workspace_ctx: &'a Option<RevsetWorkspaceContext<'a>>,
|
workspace_ctx: &'a Option<RevsetWorkspaceContext<'a>>,
|
||||||
|
/// Whether or not `kind:"pattern"` syntax is allowed.
|
||||||
|
allow_string_pattern: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ParseState<'_> {
|
impl ParseState<'_> {
|
||||||
|
@ -859,6 +846,7 @@ impl ParseState<'_> {
|
||||||
locals,
|
locals,
|
||||||
user_email: self.user_email,
|
user_email: self.user_email,
|
||||||
workspace_ctx: self.workspace_ctx,
|
workspace_ctx: self.workspace_ctx,
|
||||||
|
allow_string_pattern: self.allow_string_pattern,
|
||||||
};
|
};
|
||||||
f(expanding_state).map_err(|e| {
|
f(expanding_state).map_err(|e| {
|
||||||
RevsetParseError::with_span_and_origin(
|
RevsetParseError::with_span_and_origin(
|
||||||
|
@ -883,6 +871,21 @@ fn parse_expression_rule(
|
||||||
pairs: Pairs<Rule>,
|
pairs: Pairs<Rule>,
|
||||||
state: ParseState,
|
state: ParseState,
|
||||||
) -> Result<Rc<RevsetExpression>, RevsetParseError> {
|
) -> Result<Rc<RevsetExpression>, RevsetParseError> {
|
||||||
|
fn not_prefix_op(
|
||||||
|
op: &Pair<Rule>,
|
||||||
|
similar_op: impl Into<String>,
|
||||||
|
description: impl Into<String>,
|
||||||
|
) -> RevsetParseError {
|
||||||
|
RevsetParseError::with_span(
|
||||||
|
RevsetParseErrorKind::NotPrefixOperator {
|
||||||
|
op: op.as_str().to_owned(),
|
||||||
|
similar_op: similar_op.into(),
|
||||||
|
description: description.into(),
|
||||||
|
},
|
||||||
|
op.as_span(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
fn not_postfix_op(
|
fn not_postfix_op(
|
||||||
op: &Pair<Rule>,
|
op: &Pair<Rule>,
|
||||||
similar_op: impl Into<String>,
|
similar_op: impl Into<String>,
|
||||||
|
@ -923,13 +926,13 @@ fn parse_expression_rule(
|
||||||
.op(Op::prefix(Rule::negate_op))
|
.op(Op::prefix(Rule::negate_op))
|
||||||
// Ranges can't be nested without parentheses. Associativity doesn't matter.
|
// Ranges can't be nested without parentheses. Associativity doesn't matter.
|
||||||
.op(Op::infix(Rule::dag_range_op, Assoc::Left)
|
.op(Op::infix(Rule::dag_range_op, Assoc::Left)
|
||||||
| Op::infix(Rule::legacy_dag_range_op, Assoc::Left)
|
| Op::infix(Rule::compat_dag_range_op, Assoc::Left)
|
||||||
| Op::infix(Rule::range_op, Assoc::Left))
|
| Op::infix(Rule::range_op, Assoc::Left))
|
||||||
.op(Op::prefix(Rule::dag_range_pre_op)
|
.op(Op::prefix(Rule::dag_range_pre_op)
|
||||||
| Op::prefix(Rule::legacy_dag_range_pre_op)
|
| Op::prefix(Rule::compat_dag_range_pre_op)
|
||||||
| Op::prefix(Rule::range_pre_op))
|
| Op::prefix(Rule::range_pre_op))
|
||||||
.op(Op::postfix(Rule::dag_range_post_op)
|
.op(Op::postfix(Rule::dag_range_post_op)
|
||||||
| Op::postfix(Rule::legacy_dag_range_post_op)
|
| Op::postfix(Rule::compat_dag_range_post_op)
|
||||||
| Op::postfix(Rule::range_post_op))
|
| Op::postfix(Rule::range_post_op))
|
||||||
// Neighbors
|
// Neighbors
|
||||||
.op(Op::postfix(Rule::parents_op)
|
.op(Op::postfix(Rule::parents_op)
|
||||||
|
@ -948,13 +951,13 @@ fn parse_expression_rule(
|
||||||
.map_prefix(|op, rhs| match op.as_rule() {
|
.map_prefix(|op, rhs| match op.as_rule() {
|
||||||
Rule::negate_op => Ok(rhs?.negated()),
|
Rule::negate_op => Ok(rhs?.negated()),
|
||||||
Rule::dag_range_pre_op => Ok(rhs?.ancestors()),
|
Rule::dag_range_pre_op => Ok(rhs?.ancestors()),
|
||||||
Rule::legacy_dag_range_pre_op => Ok(rhs?.legacy_ancestors()),
|
Rule::compat_dag_range_pre_op => Err(not_prefix_op(&op, "::", "ancestors")),
|
||||||
Rule::range_pre_op => Ok(RevsetExpression::root().range(&rhs?)),
|
Rule::range_pre_op => Ok(RevsetExpression::root().range(&rhs?)),
|
||||||
r => panic!("unexpected prefix operator rule {r:?}"),
|
r => panic!("unexpected prefix operator rule {r:?}"),
|
||||||
})
|
})
|
||||||
.map_postfix(|lhs, op| match op.as_rule() {
|
.map_postfix(|lhs, op| match op.as_rule() {
|
||||||
Rule::dag_range_post_op => Ok(lhs?.descendants()),
|
Rule::dag_range_post_op => Ok(lhs?.descendants()),
|
||||||
Rule::legacy_dag_range_post_op => Ok(lhs?.legacy_descendants()),
|
Rule::compat_dag_range_post_op => Err(not_postfix_op(&op, "::", "descendants")),
|
||||||
Rule::range_post_op => Ok(lhs?.range(&RevsetExpression::visible_heads())),
|
Rule::range_post_op => Ok(lhs?.range(&RevsetExpression::visible_heads())),
|
||||||
Rule::parents_op => Ok(lhs?.parents()),
|
Rule::parents_op => Ok(lhs?.parents()),
|
||||||
Rule::children_op => Ok(lhs?.children()),
|
Rule::children_op => Ok(lhs?.children()),
|
||||||
|
@ -968,7 +971,13 @@ fn parse_expression_rule(
|
||||||
Rule::difference_op => Ok(lhs?.minus(&rhs?)),
|
Rule::difference_op => Ok(lhs?.minus(&rhs?)),
|
||||||
Rule::compat_sub_op => Err(not_infix_op(&op, "~", "difference")),
|
Rule::compat_sub_op => Err(not_infix_op(&op, "~", "difference")),
|
||||||
Rule::dag_range_op => Ok(lhs?.dag_range_to(&rhs?)),
|
Rule::dag_range_op => Ok(lhs?.dag_range_to(&rhs?)),
|
||||||
Rule::legacy_dag_range_op => Ok(lhs?.legacy_dag_range_to(&rhs?)),
|
Rule::compat_dag_range_op => {
|
||||||
|
if state.allow_string_pattern {
|
||||||
|
Ok(lhs?.legacy_dag_range_to(&rhs?))
|
||||||
|
} else {
|
||||||
|
Err(not_infix_op(&op, "::", "DAG range"))
|
||||||
|
}
|
||||||
|
}
|
||||||
Rule::range_op => Ok(lhs?.range(&rhs?)),
|
Rule::range_op => Ok(lhs?.range(&rhs?)),
|
||||||
r => panic!("unexpected infix operator rule {r:?}"),
|
r => panic!("unexpected infix operator rule {r:?}"),
|
||||||
})
|
})
|
||||||
|
@ -1471,7 +1480,11 @@ fn parse_function_argument_to_string_pattern(
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
let make_type_error = || make_error("Expected function argument of string pattern".to_owned());
|
let make_type_error = || make_error("Expected function argument of string pattern".to_owned());
|
||||||
let expression = parse_expression_rule(pair.into_inner(), state)?;
|
let expression = {
|
||||||
|
let mut inner_state = state;
|
||||||
|
inner_state.allow_string_pattern = true;
|
||||||
|
parse_expression_rule(pair.into_inner(), inner_state)?
|
||||||
|
};
|
||||||
let pattern = match expression.as_ref() {
|
let pattern = match expression.as_ref() {
|
||||||
RevsetExpression::CommitRef(RevsetCommitRef::Symbol(symbol)) => {
|
RevsetExpression::CommitRef(RevsetCommitRef::Symbol(symbol)) => {
|
||||||
let needle = symbol.to_owned();
|
let needle = symbol.to_owned();
|
||||||
|
@ -1515,7 +1528,12 @@ fn parse_function_argument_as_literal<T: FromStr>(
|
||||||
span,
|
span,
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
let expression = parse_expression_rule(pair.into_inner(), state)?;
|
let expression = {
|
||||||
|
// Don't suggest :: operator for :, which is invalid in this context.
|
||||||
|
let mut inner_state = state;
|
||||||
|
inner_state.allow_string_pattern = true;
|
||||||
|
parse_expression_rule(pair.into_inner(), inner_state)?
|
||||||
|
};
|
||||||
match expression.as_ref() {
|
match expression.as_ref() {
|
||||||
RevsetExpression::CommitRef(RevsetCommitRef::Symbol(symbol)) => {
|
RevsetExpression::CommitRef(RevsetCommitRef::Symbol(symbol)) => {
|
||||||
symbol.parse().map_err(|_| make_error())
|
symbol.parse().map_err(|_| make_error())
|
||||||
|
@ -1534,6 +1552,7 @@ pub fn parse(
|
||||||
locals: &HashMap::new(),
|
locals: &HashMap::new(),
|
||||||
user_email: &context.user_email,
|
user_email: &context.user_email,
|
||||||
workspace_ctx: &context.workspace,
|
workspace_ctx: &context.workspace,
|
||||||
|
allow_string_pattern: false,
|
||||||
};
|
};
|
||||||
parse_program(revset_str, state)
|
parse_program(revset_str, state)
|
||||||
}
|
}
|
||||||
|
@ -2960,6 +2979,14 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_parse_revset_compat_operator() {
|
fn test_parse_revset_compat_operator() {
|
||||||
|
assert_eq!(
|
||||||
|
parse(":foo"),
|
||||||
|
Err(RevsetParseErrorKind::NotPrefixOperator {
|
||||||
|
op: ":".to_owned(),
|
||||||
|
similar_op: "::".to_owned(),
|
||||||
|
description: "ancestors".to_owned(),
|
||||||
|
})
|
||||||
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
parse("foo^"),
|
parse("foo^"),
|
||||||
Err(RevsetParseErrorKind::NotPostfixOperator {
|
Err(RevsetParseErrorKind::NotPostfixOperator {
|
||||||
|
|
Loading…
Reference in a new issue