Repository: djc/askama (rust)

Issue:
Error "proc-macro derive produced unparseable tokens" with nested macro call
I was working on updating [UniFFI](https://github.com/mozilla/uniffi-rs/) to the latest `main` branch of Askama, and ran into a case where the `#[derive(Template)]` seems to generate invalid code. Below is a reduced example:

```
use askama::Template;

#[derive(Template)]
#[template(source="

use askama::Template;

#[derive(Template)]
#[template(source="

    {%- macro outer(ctx) -%}
    {%- call inner(ctx, \"name\") -%}
    {%- endmacro -%}

    {%- macro inner(ctx, name) -%}
    {{ name }} = value;
    {%- endmacro -%}

    {%- call outer(ctx) -%}

", ext="txt", print="code")]
struct MacroBug {
    ctx: (),
}

impl MacroBug {
    #[cfg(test)]
    fn testme() -> String {
        MacroBug { ctx: () }.render().unwrap()
    }
}

#[cfg(test)]
mod test {
    use super::*;
    #[test]
    fn test_it() {
        assert_eq!(MacroBug::testme(), "name = value;")
    }
}
```

This compiles and runs correctly on the latest Askama release, but when compiled against current `main` ([b318d7cb](https://github.com/djc/askama/commit/b318d7cbcded2c6dfc66bbe19687f1246a9a9eab)) it gives an error:

```
error: expected pattern, found `,`
 --> src/lib.rs:4:10
  |
4 | #[derive(Template)]
  |          ^^^^^^^^ expected pattern
  |
  = note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info)

error: proc-macro derive produced unparseable tokens
 --> src/lib.rs:4:10
  |
4 | #[derive(Template)]
  |          ^^^^^^^^
```

The issue seems to be in the code for calling the inner macro. The generated code front `print="code"` looks like:

```
impl ::askama::Template for MacroBug {
    fn render_into(&self, writer: &mut dyn ::std::fmt::Write) -> ::askama::Result<()> {
        {
            {
                let (, name) = (, ("name"));
```

Pull request description:
Fix code generation for macro calls that store args in variables.
This fixes #498 for me locally. I don't know enough about the codebase to know how to write a good test for this or whether there's a cleaner way to fix it. But I'll be happy to polish this up a bit if you can give me a few pointers.

Patch:
diff --git a/askama_shared/src/generator.rs b/askama_shared/src/generator.rs
index 638a25f..b0489ec 100644
--- a/askama_shared/src/generator.rs
+++ b/askama_shared/src/generator.rs
@@ -717,6 +717,7 @@ impl<'a, S: std::hash::BuildHasher> Generator<'a, S> {
 
         let mut names = Buffer::new(0);
         let mut values = Buffer::new(0);
+        let mut is_first_variable = true;
         for (i, arg) in def.args.iter().enumerate() {
             let expr = args.get(i).ok_or_else(|| {
                 CompileError::String(format!("macro '{}' takes more than {} arguments", name, i))
@@ -742,7 +743,9 @@ impl<'a, S: std::hash::BuildHasher> Generator<'a, S> {
                 // multiple times, e.g. in the case of macro
                 // parameters being used multiple times.
                 _ => {
-                    if i > 0 {
+                    if is_first_variable {
+                        is_first_variable = false
+                    } else {
                         names.write(", ");
                         values.write(", ");
                     }


Test patch:
diff --git a/testing/templates/nested-macro-args.html b/testing/templates/nested-macro-args.html
new file mode 100644
index 0000000..03826f8
--- /dev/null
+++ b/testing/templates/nested-macro-args.html
@@ -0,0 +1,9 @@
+{%- macro outer(first) -%}
+{%- call inner(first, "second") -%}
+{%- endmacro -%}
+
+{%- macro inner(first, second) -%}
+{{ first }} {{ second }}
+{%- endmacro -%}
+
+{%- call outer("first") -%}
diff --git a/testing/tests/macro.rs b/testing/tests/macro.rs
index 459b1e2..7f7e4dc 100644
--- a/testing/tests/macro.rs
+++ b/testing/tests/macro.rs
@@ -53,3 +53,13 @@ fn test_short_circuit() {
     let t = ShortCircuitTemplate {};
     assert_eq!(t.render().unwrap(), "truetruetruefalsetruetrue");
 }
+
+#[derive(Template)]
+#[template(path = "nested-macro-args.html")]
+struct NestedMacroArgsTemplate {}
+
+#[test]
+fn test_nested_macro_with_args() {
+    let t = NestedMacroArgsTemplate {};
+    assert_eq!(t.render().unwrap(), "first second");
+}