diff --git a/askama_derive/src/generator/node.rs b/askama_derive/src/generator/node.rs index 916e2bbe..9df94ef2 100644 --- a/askama_derive/src/generator/node.rs +++ b/askama_derive/src/generator/node.rs @@ -608,7 +608,7 @@ impl<'a> Generator<'a, '_> { call: &'a WithSpan<'a, Call<'_>>, ) -> Result { let Call { - ws, + ws1, scope, name, ref args, @@ -650,7 +650,7 @@ impl<'a> Generator<'a, '_> { self.seen_macros.push((def, ctx.file_info_of(call.span()))); } self.seen_callers.push(call); - self.flush_ws(ws); // Cannot handle_ws() here: whitespace from macro definition comes first + self.flush_ws(ws1); // Cannot handle_ws() here: whitespace from macro definition comes first let size_hint = self.push_locals(|this| { macro_call_ensure_arg_count(call, def, ctx)?; @@ -771,7 +771,7 @@ impl<'a> Generator<'a, '_> { buf.write('}'); Ok(size_hint) })?; - self.prepare_ws(ws); + self.prepare_ws(ws1); self.seen_macros.pop(); self.seen_callers.pop(); Ok(size_hint) @@ -1119,9 +1119,10 @@ impl<'a> Generator<'a, '_> { if ***path == Expr::Var("super") { return self.write_block(ctx, buf, None, ws, s.span()); } else if ***path == Expr::Var("caller") { - let def = self.seen_callers.last().ok_or_else(|| { + let def = self.seen_callers.pop().ok_or_else(|| { ctx.generate_error(format_args!("block is not defined for caller"), s.span()) })?; + self.handle_ws(ws); let size_hint = self.push_locals(|this| { this.write_buf_writable(ctx, buf)?; buf.write('{'); @@ -1180,14 +1181,14 @@ impl<'a> Generator<'a, '_> { } } } - let mut size_hint = this.handle(ctx, &def.nodes, buf, AstLevel::Nested)?; + this.flush_ws(def.ws2); size_hint += this.write_buf_writable(ctx, buf)?; buf.write('}'); Ok(size_hint) })?; - self.prepare_ws(ws); + self.seen_callers.push(def); return Ok(size_hint); } } diff --git a/askama_parser/src/node.rs b/askama_parser/src/node.rs index ec17abe9..c006dc3e 100644 --- a/askama_parser/src/node.rs +++ b/askama_parser/src/node.rs @@ -877,12 +877,11 @@ impl<'a> Import<'a> { #[derive(Debug, PartialEq)] pub struct Call<'a> { - pub ws: Ws, + pub ws1: Ws, pub caller_args: Vec<&'a str>, pub scope: Option<&'a str>, pub name: &'a str, pub args: Vec>>, - pub ws1: Ws, pub nodes: Vec>, pub ws2: Ws, } @@ -947,12 +946,11 @@ impl<'a> Call<'a> { Ok(WithSpan::new( Self { - ws: Ws(pws, nws), + ws1: Ws(pws, nws), caller_args: call_args.unwrap_or_default(), scope, name, args, - ws1: Ws(pws, pws2), nodes, ws2: Ws(pws2, nws2), }, diff --git a/testing/tests/calls.rs b/testing/tests/calls.rs index 34d9d827..9d6dc262 100644 --- a/testing/tests/calls.rs +++ b/testing/tests/calls.rs @@ -125,17 +125,18 @@ nested assert_eq!(x.render().unwrap(), "nested"); } + #[test] fn test_caller_struct() { struct TestInput<'a> { - a: &'a str, - b: &'a str + a: &'a str, + b: &'a str, } #[derive(Template)] #[template( source = r#" {%- macro test(a) -%} -{{caller(a)}} +{{- caller(a) -}} {%- endmacro -%} {%- call(value) test(a) -%} a: {{value.a}} @@ -145,13 +146,10 @@ b: {{value.b}} ext = "txt" )] struct Tmpl<'a> { - a: TestInput<'a> + a: TestInput<'a>, } let x = Tmpl { - a: TestInput { - a: "one", - b: "two" - } + a: TestInput { a: "one", b: "two" }, }; assert_eq!(x.render().unwrap(), "a: one\nb: two"); } @@ -162,18 +160,18 @@ fn test_caller_args() { #[template( source = r#" {%- macro test() -%} -{{- caller("test") -}} -{{- caller(1) -}} +{{~ caller("test") ~}} +{{~ caller(1) ~}} {%- endmacro -%} {%- call(value) test() -%} -nested {{value}} +nested {{value}} {%- endcall -%} "#, ext = "html" )] struct CallerEmpty {} let x = CallerEmpty {}; - assert_eq!(x.render().unwrap(), "nested testnested 1"); + assert_eq!(x.render().unwrap(), "nested test\nnested 1"); } // Ensures that fields are not moved when calling a jinja macro. diff --git a/testing/tests/macro.rs b/testing/tests/macro.rs index 73b8bfca..79042646 100644 --- a/testing/tests/macro.rs +++ b/testing/tests/macro.rs @@ -238,6 +238,48 @@ fn test_default_value3() { ); } +// This test a caller expression with expressions in the arguments. +#[test] +fn test_caller_expr() { + #[derive(Template)] + #[template( + source = "{%- macro thrice(a, b, c) -%} +{{caller(a+10,b - 10,a+b+c)}} +{% endmacro -%} +{%- call(a,b,c) thrice(10,11,13) -%}{{a}} {{b}} {{c + 1}}{%- endcall -%} +", + ext = "html" + )] + struct MacroCallerExpr; + assert_eq!(MacroCallerExpr.render().unwrap(), "20 1 35\n"); + +} + +#[test] +fn test_caller_in_caller() { + #[derive(Template)] + #[template( + source = r#" + {%- macro test2() -%} + {{~ caller("bb") ~}} + {%- endmacro -%} + {%- macro test() -%} + {{~ caller("a") ~}} + {%- endmacro -%} + {%- call(a) test() -%} + {%- call(b) test2() -%} + one: {{ b }} + {%- endcall -%} + two: {{- a -}} + {%- endcall -%} + "#, + ext = "txt" + )] + struct CallerInCaller; + assert_eq!(CallerInCaller.render().unwrap(), "one: bbtwo:a"); +} + + // This test ensures that we can use declared variables as default value for // macro arguments. #[test] diff --git a/testing/tests/ui/caller_arguments.rs b/testing/tests/ui/caller_arguments.rs new file mode 100644 index 00000000..a76f375f --- /dev/null +++ b/testing/tests/ui/caller_arguments.rs @@ -0,0 +1,70 @@ +use askama::Template; + +#[derive(Template)] +#[template( + source = r#" + {% macro test() %} + {{- caller("a", "b") -}} + {%- endmacro -%} + {%- call(a,b,c) test() -%} + {{- a -}} + {%- endcall -%} + "#, + ext = "txt" +)] +struct InvalidNumberArguments { +} + +#[derive(Template)] +#[template( + source = r#" + {% macro test() %} + {{- caller("a") -}} + {%- endmacro -%} + {%- call(a test() -%} + {{- a -}} + {%- endcall -%} + "#, + ext = "txt" +)] +struct NoClosingParen { +} + +#[derive(Template)] +#[template( + source = r#" + {% macro test() %} + {{- caller("a") -}} + {%- endmacro -%} + {%- call(a) test() -%} + {{- caller(a) -}} + {%- endcall -%} + "#, + ext = "txt" +)] +struct CallerInCaller { +} + +#[derive(Template)] +#[template( + source = r#" + {% macro test2() %} + {{ caller("bb") }} + {% endmacro %} + {% macro test() %} + {{ caller("a") }} + {%- endmacro -%} + {%- call(a) test() -%} + {% call(b) test2() %} + {{ caller("b") }} + {% endcall %} + {{- a -}} + {%- endcall -%} + "#, + ext = "txt" +)] +struct CallerInCaller1 { +} + +fn main() {} + diff --git a/testing/tests/ui/caller_arguments.stderr b/testing/tests/ui/caller_arguments.stderr new file mode 100644 index 00000000..0c0a1b6c --- /dev/null +++ b/testing/tests/ui/caller_arguments.stderr @@ -0,0 +1,59 @@ +error: missing `c` argument + --> InvalidNumberArguments.txt:3:18 + "(\"a\", \"b\") -}}\n {%- endmacro -%}\n {%- call(a,b,c) test() -%}\n {{- a"... + --> tests/ui/caller_arguments.rs:5:14 + | +5 | source = r#" + | ______________^ +6 | | {% macro test() %} +7 | | {{- caller("a", "b") -}} +8 | | {%- endmacro -%} +... | +11 | | {%- endcall -%} +12 | | "#, + | |______^ + +error: expected `)` to close call argument list + --> :5:15 + "test() -%}\n {{- a -}}\n {%- endcall -%}\n " + --> tests/ui/caller_arguments.rs:20:14 + | +20 | source = r#" + | ______________^ +21 | | {% macro test() %} +22 | | {{- caller("a") -}} +23 | | {%- endmacro -%} +... | +26 | | {%- endcall -%} +27 | | "#, + | |______^ + +error: block is not defined for caller + --> CallerInCaller.txt:6:18 + "(a) -}}\n {%- endcall -%}\n " + --> tests/ui/caller_arguments.rs:35:14 + | +35 | source = r#" + | ______________^ +36 | | {% macro test() %} +37 | | {{- caller("a") -}} +38 | | {%- endmacro -%} +... | +41 | | {%- endcall -%} +42 | | "#, + | |______^ + +error: block is not defined for caller + --> CallerInCaller1.txt:10:21 + "(\"b\") }}\n {% endcall %}\n {{- a -}}\n {%- endcall -%}\n " + --> tests/ui/caller_arguments.rs:50:14 + | +50 | source = r#" + | ______________^ +51 | | {% macro test2() %} +52 | | {{ caller("bb") }} +53 | | {% endmacro %} +... | +62 | | {%- endcall -%} +63 | | "#, + | |______^