Repository: jg-rp/liquid

Issue:
Static analysis of `macro` nodes
`Macro.block_scope()` does not include `args` and `kwargs`, so will incorrectly identify occurrences of `args` and `kwargs` as "global" variables in macro blocks.

Discussion:

Solution patch:
diff --git a/CHANGES.md b/CHANGES.md
index 72970c2..ffb3e0e 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -3,6 +3,7 @@
 ## Version 2.0.2 (unreleased)
 
 - Fixed static analysis of filters in ternary expressions. See [#180](https://github.com/jg-rp/liquid/issues/180).
+- Fixed static analysis of macro blocks. Previously `args` and `kwargs` were considered "global". See [#181](https://github.com/jg-rp/liquid/issues/181).
 
 ## Version 2.0.1
 
diff --git a/liquid/extra/tags/macro_tag.py b/liquid/extra/tags/macro_tag.py
index 9940d71..1001292 100644
--- a/liquid/extra/tags/macro_tag.py
+++ b/liquid/extra/tags/macro_tag.py
@@ -94,6 +94,8 @@ class MacroNode(Node):
 
     def block_scope(self) -> Iterable[Identifier]:
         """Return variables this node adds to the node's block scope."""
+        yield Identifier("args", token=self.token)
+        yield Identifier("kwargs", token=self.token)
         yield from (Identifier(p.name, token=p.token) for p in self.args.values())
 
 


Test patch:
diff --git a/tests/test_static_analysis.py b/tests/test_static_analysis.py
index 2864b19..80cd40c 100644
--- a/tests/test_static_analysis.py
+++ b/tests/test_static_analysis.py
@@ -864,6 +864,7 @@ def test_analyze_macro_and_call(env: Environment) -> None:
     source = (
         r"{% macro 'foo', you: 'World', arg: n %}"
         r"Hello, {{ you }}!"
+        r"{{ args.size }}"
         r"{% endmacro %}"
         r"{% call 'foo' %}"
         r"{% assign x = 'you' %}"
@@ -875,13 +876,18 @@ def test_analyze_macro_and_call(env: Environment) -> None:
 
     _assert(
         env.from_string(source),
-        locals={"x": [Variable(["x"], Span("", 96))]},
+        locals={"x": [Variable(["x"], Span("", 111))]},
         globals={"n": n},
-        variables={"n": n, "you": you, "x": [Variable(["x"], Span("", 128))]},
+        variables={
+            "n": n,
+            "you": you,
+            "args": [Variable(["args", "size"], Span("", 59))],
+            "x": [Variable(["x"], Span("", 143))],
+        },
         tags={
             "macro": [Span("", 3)],
-            "call": [Span("", 73), Span("", 111)],
-            "assign": [Span("", 89)],
+            "call": [Span("", 88), Span("", 126)],
+            "assign": [Span("", 104)],
         },
     )