Repository: mitsuhiko/minijinja (language: rust)

Issue:
A `{% do var.method() %}` fails when used within the content of a  `{% call %}`
## Description

It appears that a `{% do %}` tag does not do a lookup of the context when it is used within the content of a `{% call %}`. The expected behavior is exhibited by a `{{..}}` and a `{% set %}` in those situations but a  `{% do %}` errors. 

This is needed for my use case: I want to be able to use  `{% do %}` for a side-effect method call within the `caller()` a `{% call %}`. At present my work around is to use a `{{..}}` instead.

## Reproduction steps

```rust
#[cfg(test)]
mod minijinja_tests {
    use derive_more::Display;
    use minijinja::{
        context,
        value::{Object, Value},
        Environment, Error, State,
    };

    /// Custom dynamic object
    #[derive(Debug, Display)]
    struct CustomObject;

    impl Object for CustomObject {
        fn call_method(&self, _state: &State, name: &str, _args: &[Value]) -> Result<Value, Error> {
            Ok(Value::from(format!("{name} was called")))
        }
    }

    #[test]
    fn can_use_do_within_call() -> Result<(), Error> {
        let mut env = Environment::new();
        env.add_template(
            "macros",
            "{% macro my_macro() -%}{{ caller() }}{%- endmacro %}",
        )?;

        let context = context!(
            var => Value::from_object(CustomObject)
        );

        // Calling the method within an `EmitExpr` works
        assert_eq!(
            env.render_str("{{ var.method() }}", &context)?,
            "method was called"
        );

        // Calling the method within an `EmitExpr` within a `Call` works
        assert_eq!(
            env.render_str(
                "{% from 'macros' import my_macro %}{% call my_macro() %}{{ var.method() }}{% endcall %}",
                &context
            )?,
            "method was called"
        );

        // Calling the method within a `Do` works
        env.render_str("{% do var.method() %}", &context)?;

        // Calling the method within a `Set` within a `Call` works
        env.render_str(
            "{% from 'macros' import my_macro %}{% call my_macro() %}{% set _ = var.method() %}{% endcall %}",
            &context
        )?;

        // Calling the method within a `Do` within a `Call` fails with error
        // Error: Error { kind: InvalidOperation, detail: "object has no method named method", name: "<string>", line: 1 }
        //
        // ---------------------------------- <string> -----------------------------------
        //     1 > {% from 'macros' import my_macro %}{% call my_macro() %}{% do var.method() %}{% endcall %}
        //         i                                                                  ^^^^^^^^^ invalid operation
        // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        // No referenced variables
        // -------------------------------------------------------------------------------
        env.render_str(
            "{% from 'macros' import my_macro %}{% call my_macro() %}{% do var.method() %}{% endcall %}",
            &context
        )?;

        Ok(())
    }
}
```

Additional helpful information:

- Version of minijinja: 0.30.4
- Version of rustc: 1.67.0 (fc594f156 2023-01-24)
- Operating system and version: Ubunutu 22.04

## What did you expect

A `{% do %}` should look up variables from the context when used within the content of a `{% call %}` block.




Discussion / hints:

Fix patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 4e8be36f..037fd37c 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,10 @@
 
 All notable changes to MiniJinja are documented here.
 
+## 0.30.6
+
+- Resolve bad closure being generated for `do` blocks.
+
 ## 0.30.5
 
 - Small performance improvements for when `preserve_order` is used by
diff --git a/minijinja/src/compiler/meta.rs b/minijinja/src/compiler/meta.rs
index 9b651ce9..1f70d2a5 100644
--- a/minijinja/src/compiler/meta.rs
+++ b/minijinja/src/compiler/meta.rs
@@ -172,7 +172,10 @@ pub fn find_macro_closure<'a>(m: &ast::Macro<'a>) -> HashSet<&'a str> {
             }
             #[cfg(feature = "macros")]
             ast::Stmt::CallBlock(_) => {}
-            ast::Stmt::Do(_) => {}
+            ast::Stmt::Do(stmt) => {
+                visit_expr(&stmt.call.expr, state);
+                stmt.call.args.iter().for_each(|x| visit_expr(x, state));
+            }
         }
     }
 


Test patch:
diff --git a/minijinja/tests/inputs/do_closure.txt b/minijinja/tests/inputs/do_closure.txt
new file mode 100644
index 00000000..20fdf6fe
--- /dev/null
+++ b/minijinja/tests/inputs/do_closure.txt
@@ -0,0 +1,6 @@
+{}
+---
+{% macro other_macro() %}23{% endmacro %}
+{% macro foo() %}[{% do other_macro() %}]{% endmacro %}
+{% set other_macro = 42 %}
+Should not show output of other_macro: {{ foo() }}
\ No newline at end of file
diff --git a/minijinja/tests/snapshots/test_templates__vm@do_closure.txt.snap b/minijinja/tests/snapshots/test_templates__vm@do_closure.txt.snap
new file mode 100644
index 00000000..5edc374b
--- /dev/null
+++ b/minijinja/tests/snapshots/test_templates__vm@do_closure.txt.snap
@@ -0,0 +1,11 @@
+---
+source: minijinja/tests/test_templates.rs
+description: "{% macro other_macro() %}23{% endmacro %}\n{% macro foo() %}[{% do other_macro() %}]{% endmacro %}\n{% set other_macro = 42 %}\nShould not show output of other_macro: {{ foo() }}"
+info: {}
+input_file: minijinja/tests/inputs/do_closure.txt
+---
+
+
+
+Should not show output of other_macro: []
+