ok/jj
1
0
Fork 0
forked from mirrors/jj

templater: report invalid argument count as error

We'll probably need a helper to extract N..M arguments, but let's revisit
it later.
This commit is contained in:
Yuya Nishihara 2023-02-02 20:21:00 +09:00
parent baf911459f
commit 45a018ec6b
2 changed files with 62 additions and 16 deletions

View file

@ -53,6 +53,11 @@ pub enum TemplateParseErrorKind {
NoSuchFunction(String), NoSuchFunction(String),
#[error(r#"Method "{name}" doesn't exist for type "{type_name}""#)] #[error(r#"Method "{name}" doesn't exist for type "{type_name}""#)]
NoSuchMethod { type_name: String, name: String }, NoSuchMethod { type_name: String, name: String },
#[error(
"Expected {} arguments",
if min == max { format!("{min}") } else { format!("{min} to {max}") },
)]
InvalidArgumentCount { min: usize, max: usize },
#[error(r#"Expected argument of type "{0}""#)] #[error(r#"Expected argument of type "{0}""#)]
InvalidArgumentType(String), InvalidArgumentType(String),
} }
@ -92,6 +97,13 @@ impl TemplateParseError {
) )
} }
fn invalid_argument_count(min: usize, max: usize, span: pest::Span<'_>) -> Self {
TemplateParseError::with_span(
TemplateParseErrorKind::InvalidArgumentCount { min, max },
span,
)
}
fn invalid_argument_type(expected_type_name: impl Into<String>, span: pest::Span<'_>) -> Self { fn invalid_argument_type(expected_type_name: impl Into<String>, span: pest::Span<'_>) -> Self {
TemplateParseError::with_span( TemplateParseError::with_span(
TemplateParseErrorKind::InvalidArgumentType(expected_type_name.into()), TemplateParseErrorKind::InvalidArgumentType(expected_type_name.into()),
@ -452,26 +464,25 @@ fn parse_commit_term<'a>(
Ok(Expression::Property(property)) Ok(Expression::Property(property))
} }
Rule::function => { Rule::function => {
let (name, mut args) = { let (name, args_span, mut args) = {
let mut inner = expr.into_inner(); let mut inner = expr.into_inner();
let name = inner.next().unwrap(); let name = inner.next().unwrap();
let args_pair = inner.next().unwrap(); let args_pair = inner.next().unwrap();
assert_eq!(name.as_rule(), Rule::identifier); assert_eq!(name.as_rule(), Rule::identifier);
assert_eq!(args_pair.as_rule(), Rule::function_arguments); assert_eq!(args_pair.as_rule(), Rule::function_arguments);
(name, args_pair.into_inner()) (name, args_pair.as_span(), args_pair.into_inner())
}; };
let expression = match name.as_str() { let expression = match name.as_str() {
"label" => { "label" => {
let label_pair = args.next().unwrap(); let arg_count_error =
|| TemplateParseError::invalid_argument_count(2, 2, args_span);
let label_pair = args.next().ok_or_else(arg_count_error)?;
let label_property = let label_property =
parse_commit_template_rule(repo, workspace_id, label_pair)? parse_commit_template_rule(repo, workspace_id, label_pair)?
.into_plain_text(); .into_plain_text();
let arg_template = match args.next() { let arg_template = args.next().ok_or_else(arg_count_error)?;
None => panic!("label() requires two arguments"),
Some(pair) => pair,
};
if args.next().is_some() { if args.next().is_some() {
panic!("label() accepts only two arguments") return Err(arg_count_error());
} }
let content = parse_commit_template_rule(repo, workspace_id, arg_template)? let content = parse_commit_template_rule(repo, workspace_id, arg_template)?
.into_template(); .into_template();
@ -482,7 +493,9 @@ fn parse_commit_term<'a>(
Expression::Template(template) Expression::Template(template)
} }
"if" => { "if" => {
let condition_pair = args.next().unwrap(); let arg_count_error =
|| TemplateParseError::invalid_argument_count(2, 3, args_span);
let condition_pair = args.next().ok_or_else(arg_count_error)?;
let condition_span = condition_pair.as_span(); let condition_span = condition_pair.as_span();
let condition = parse_commit_template_rule(repo, workspace_id, condition_pair)? let condition = parse_commit_template_rule(repo, workspace_id, condition_pair)?
.try_into_boolean() .try_into_boolean()
@ -490,19 +503,18 @@ fn parse_commit_term<'a>(
TemplateParseError::invalid_argument_type("Boolean", condition_span) TemplateParseError::invalid_argument_type("Boolean", condition_span)
})?; })?;
let true_template = match args.next() { let true_template = args
None => panic!("if() requires at least two arguments"), .next()
Some(pair) => { .ok_or_else(arg_count_error)
parse_commit_template_rule(repo, workspace_id, pair)?.into_template() .and_then(|pair| parse_commit_template_rule(repo, workspace_id, pair))?
} .into_template();
};
let false_template = args let false_template = args
.next() .next()
.map(|pair| parse_commit_template_rule(repo, workspace_id, pair)) .map(|pair| parse_commit_template_rule(repo, workspace_id, pair))
.transpose()? .transpose()?
.map(|x| x.into_template()); .map(|x| x.into_template());
if args.next().is_some() { if args.next().is_some() {
panic!("if() accepts at most three arguments") return Err(arg_count_error());
} }
let template = Box::new(ConditionalTemplate::new( let template = Box::new(ConditionalTemplate::new(
condition, condition,

View file

@ -148,6 +148,40 @@ fn test_templater_parse_error() {
= Method "foo" doesn't exist for type "String" = Method "foo" doesn't exist for type "String"
"###); "###);
insta::assert_snapshot!(render_err(r#"label()"#), @r###"
Error: Failed to parse template: --> 1:7
|
1 | label()
| ^
|
= Expected 2 arguments
"###);
insta::assert_snapshot!(render_err(r#"label("foo", "bar", "baz")"#), @r###"
Error: Failed to parse template: --> 1:7
|
1 | label("foo", "bar", "baz")
| ^-----------------^
|
= Expected 2 arguments
"###);
insta::assert_snapshot!(render_err(r#"if()"#), @r###"
Error: Failed to parse template: --> 1:4
|
1 | if()
| ^
|
= Expected 2 to 3 arguments
"###);
insta::assert_snapshot!(render_err(r#"if("foo", "bar", "baz", "quux")"#), @r###"
Error: Failed to parse template: --> 1:4
|
1 | if("foo", "bar", "baz", "quux")
| ^-------------------------^
|
= Expected 2 to 3 arguments
"###);
insta::assert_snapshot!(render_err(r#"if(label("foo", "bar"), "baz")"#), @r###" insta::assert_snapshot!(render_err(r#"if(label("foo", "bar"), "baz")"#), @r###"
Error: Failed to parse template: --> 1:4 Error: Failed to parse template: --> 1:4
| |