Fix #531: Allow named arguments in expr macro calls

This commit is contained in:
Markus Ebner 2025-07-22 12:33:26 +02:00
parent 0613a509b6
commit 48d5dba024
No known key found for this signature in database
GPG Key ID: 2F4B02774683BA74
6 changed files with 25 additions and 64 deletions

View File

@ -485,6 +485,14 @@ impl<'a> Generator<'a, '_> {
left: &WithSpan<'a, Expr<'a>>, left: &WithSpan<'a, Expr<'a>>,
args: &[WithSpan<'a, Expr<'a>>], args: &[WithSpan<'a, Expr<'a>>],
) -> Result<DisplayWrap, CompileError> { ) -> Result<DisplayWrap, CompileError> {
// ensure that no named args are used in normal rust call expressions
if let Some(arg) = args
.iter()
.find(|&arg| matches!(**arg, Expr::NamedArgument(_, _)))
{
return Err(ctx.generate_error("Unsupported use of named arguments", arg.span()));
}
match &**left { match &**left {
Expr::AssociatedItem(sub_left, AssociatedItem { name, generics }) Expr::AssociatedItem(sub_left, AssociatedItem { name, generics })
if ***sub_left == Expr::Var("loop") => if ***sub_left == Expr::Var("loop") =>

View File

@ -251,7 +251,6 @@ impl<'a> Expr<'a> {
pub(super) fn arguments( pub(super) fn arguments(
i: &mut &'a str, i: &mut &'a str,
level: Level<'_>, level: Level<'_>,
is_template_macro: bool,
) -> ParseResult<'a, Vec<WithSpan<'a, Self>>> { ) -> ParseResult<'a, Vec<WithSpan<'a, Self>>> {
let _level_guard = level.nest(i)?; let _level_guard = level.nest(i)?;
let mut named_arguments = HashSet::new(); let mut named_arguments = HashSet::new();
@ -269,15 +268,7 @@ impl<'a> Expr<'a> {
let has_named_arguments = !named_arguments.is_empty(); let has_named_arguments = !named_arguments.is_empty();
let expr = alt(( let expr = alt((
move |i: &mut _| { move |i: &mut _| Self::named_argument(i, level, named_arguments, start),
Self::named_argument(
i,
level,
named_arguments,
start,
is_template_macro,
)
},
move |i: &mut _| Self::parse(i, level, false), move |i: &mut _| Self::parse(i, level, false),
)) ))
.parse_next(i)?; .parse_next(i)?;
@ -300,14 +291,7 @@ impl<'a> Expr<'a> {
level: Level<'_>, level: Level<'_>,
named_arguments: &mut HashSet<&'a str>, named_arguments: &mut HashSet<&'a str>,
start: &'a str, start: &'a str,
is_template_macro: bool,
) -> ParseResult<'a, WithSpan<'a, Self>> { ) -> ParseResult<'a, WithSpan<'a, Self>> {
if !is_template_macro {
// If this is not a template macro, we don't want to parse named arguments so
// we instead return an error which will allow to continue the parsing.
return fail.parse_next(i);
}
let (argument, _, value) = (identifier, ws('='), move |i: &mut _| { let (argument, _, value) = (identifier, ws('='), move |i: &mut _| {
Self::parse(i, level, false) Self::parse(i, level, false)
}) })
@ -703,7 +687,7 @@ impl<'a> Filter<'a> {
pub(crate) fn parse(i: &mut &'a str, level: Level<'_>) -> ParseResult<'a, Self> { pub(crate) fn parse(i: &mut &'a str, level: Level<'_>) -> ParseResult<'a, Self> {
let (name, arguments) = ( let (name, arguments) = (
ws(|i: &mut _| path_or_identifier(i, level)), ws(|i: &mut _| path_or_identifier(i, level)),
opt(|i: &mut _| Expr::arguments(i, level, true)), opt(|i: &mut _| Expr::arguments(i, level)),
) )
.parse_next(i)?; .parse_next(i)?;
Ok(Self { Ok(Self {
@ -1126,7 +1110,7 @@ impl<'a> Suffix<'a> {
fn call(i: &mut &'a str, level: Level<'_>) -> ParseResult<'a, Self> { fn call(i: &mut &'a str, level: Level<'_>) -> ParseResult<'a, Self> {
(opt(|i: &mut _| call_generics(i, level)), |i: &mut _| { (opt(|i: &mut _| call_generics(i, level)), |i: &mut _| {
Expr::arguments(i, level, false) Expr::arguments(i, level)
}) })
.map(|(_generics, args)| Self::Call { args }) .map(|(_generics, args)| Self::Call { args })
.parse_next(i) .parse_next(i)

View File

@ -885,7 +885,7 @@ impl<'a> Call<'a> {
( (
opt((ws(identifier), ws("::"))), opt((ws(identifier), ws("::"))),
ws(identifier), ws(identifier),
opt(ws(|nested: &mut _| Expr::arguments(nested, s.level, true))), opt(ws(|nested: &mut _| Expr::arguments(nested, s.level))),
opt(Whitespace::parse), opt(Whitespace::parse),
|i: &mut _| s.tag_block_end(i), |i: &mut _| s.tag_block_end(i),
), ),

View File

@ -89,20 +89,4 @@ struct CallerInCaller1 {
struct JustCaller{ struct JustCaller{
} }
#[derive(Template)]
#[template(
source = r#"
{% macro test() %}
{{ caller("a", one = "b") }}
{%- endmacro -%}
{%- call(two, one) test() -%}
{{- two -}} {{- one -}}
{%- endcall -%}
"#,
ext = "txt"
)]
struct NamedArguments {
}
fn main() {} fn main() {}

View File

@ -80,18 +80,3 @@ error: block is not defined for `caller`
| |
86 | source = r#"{{caller()}}"#, 86 | source = r#"{{caller()}}"#,
| ^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^
error: failed to parse template source
--> <source attribute>:3:27
"= \"b\") }}\n {%- endmacro -%}\n {%- call(two, one) test() -%}\n {{- two"...
--> tests/ui/caller_arguments.rs:94:14
|
94 | source = r#"
| ______________^
95 | | {% macro test() %}
96 | | {{ caller("a", one = "b") }}
97 | | {%- endmacro -%}
... |
100 | | {%- endcall -%}
101 | | "#,
| |______^

View File

@ -1,30 +1,30 @@
error: failed to parse template source error: Unsupported use of named arguments
--> <source attribute>:1:15 --> NamedArgsInRustExprMemberFn.html:1:10
"= 5) }}" "(arg = 5) }}"
--> tests/ui/expr_fn_calls.rs:10:21 --> tests/ui/expr_fn_calls.rs:10:21
| |
10 | #[template(source = "{{ test_fn(arg = 5) }}", ext = "html")] 10 | #[template(source = "{{ test_fn(arg = 5) }}", ext = "html")]
| ^^^^^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^^^^^
error: failed to parse template source error: Unsupported use of named arguments
--> <source attribute>:1:23 --> NamedArgsInRustExprStaticCall.html:1:18
"= \"test\") }}" "(arg = \"test\") }}"
--> tests/ui/expr_fn_calls.rs:21:21 --> tests/ui/expr_fn_calls.rs:21:21
| |
21 | #[template(source = r#"{{ self::static_fn(arg = "test") }}"#, ext = "html")] 21 | #[template(source = r#"{{ self::static_fn(arg = "test") }}"#, ext = "html")]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: failed to parse template source error: Unsupported use of named arguments
--> <source attribute>:1:39 --> NamedArgsInRustExprStaticCall2.html:1:34
"= \"test\") }}" "(arg = \"test\") }}"
--> tests/ui/expr_fn_calls.rs:26:21 --> tests/ui/expr_fn_calls.rs:26:21
| |
26 | #[template(source = r#"{{ self::static_fn2("test").unwrap(arg = "test") }}"#, ext = "html")] 26 | #[template(source = r#"{{ self::static_fn2("test").unwrap(arg = "test") }}"#, ext = "html")]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: failed to parse template source error: Unsupported use of named arguments
--> <source attribute>:1:34 --> NamedArgsInRustExprStaticCall3.html:1:29
"= \"test\") %}" "(arg = \"test\") %}"
--> tests/ui/expr_fn_calls.rs:31:21 --> tests/ui/expr_fn_calls.rs:31:21
| |
31 | #[template(source = r#"{% let test = self::static_fn(arg = "test") %}"#, ext = "html")] 31 | #[template(source = r#"{% let test = self::static_fn(arg = "test") %}"#, ext = "html")]