From ab7012c5386f8fc31cace97fb7c6d812e627ddfb Mon Sep 17 00:00:00 2001 From: LordBaryhobal Date: Wed, 8 Jul 2026 19:21:19 +0200 Subject: [PATCH] fix(checker): leaking for-loop iterator target --- midas/checker/python.py | 10 +- midas/checker/resolver.py | 2 +- tests/cases/checker/10_variable_defined.py | 5 + .../checker/10_variable_defined.py.ref.json | 124 ++++++++++++++++++ 4 files changed, 138 insertions(+), 3 deletions(-) diff --git a/midas/checker/python.py b/midas/checker/python.py index 2ed55d1..ef8e688 100644 --- a/midas/checker/python.py +++ b/midas/checker/python.py @@ -601,6 +601,10 @@ class PythonTyper( pass def visit_for_stmt(self, stmt: p.ForStmt) -> None: + outer_env: Environment = self.env + inner_env: Environment = Environment(self.env) + self.env = inner_env + item_type: Type = UnknownType() iterator_type: Type = self.type_of(stmt.iterator) if iterator_type != UnknownType(): @@ -614,8 +618,10 @@ class PythonTyper( self._assign(stmt.location, stmt.target, item_type) self.judge(stmt.target, item_type) - env: Environment = Environment(self.env) - body_returned: bool = self.process_block(stmt.body, env) + body_returned: bool = self.process_block(stmt.body, inner_env) + + self.env = outer_env + if body_returned: raise ReturnException() diff --git a/midas/checker/resolver.py b/midas/checker/resolver.py index 1cc77db..16756fb 100644 --- a/midas/checker/resolver.py +++ b/midas/checker/resolver.py @@ -180,9 +180,9 @@ class Resolver(p.Stmt.Visitor[None], p.Expr.Visitor[None]): pass def visit_for_stmt(self, stmt: p.ForStmt) -> None: + self.begin_scope() self.resolve(stmt.iterator) self._visit_assign(stmt.target) - self.begin_scope() self.resolve(*stmt.body) self.end_scope() diff --git a/tests/cases/checker/10_variable_defined.py b/tests/cases/checker/10_variable_defined.py index 41fef83..47b8d56 100644 --- a/tests/cases/checker/10_variable_defined.py +++ b/tests/cases/checker/10_variable_defined.py @@ -38,3 +38,8 @@ def fact(n: int) -> int: if n <= 1: return 1 return n * fact(n - 1) + + +for i in [1, 2, 3]: + _ = i +_ = i diff --git a/tests/cases/checker/10_variable_defined.py.ref.json b/tests/cases/checker/10_variable_defined.py.ref.json index 28af712..40428e2 100644 --- a/tests/cases/checker/10_variable_defined.py.ref.json +++ b/tests/cases/checker/10_variable_defined.py.ref.json @@ -83,6 +83,20 @@ ] }, "message": "Unknown variable" + }, + { + "type": "Warning", + "location": { + "start": [ + 45, + 4 + ], + "end": [ + 45, + 5 + ] + }, + "message": "Unknown variable" } ], "judgments": [ @@ -522,6 +536,116 @@ "type": { "name": "int" } + }, + { + "location": { + "from": "L43:10", + "to": "L43:11" + }, + "expr": { + "_type": "LiteralExpr", + "value": 1 + }, + "type": { + "name": "int" + } + }, + { + "location": { + "from": "L43:13", + "to": "L43:14" + }, + "expr": { + "_type": "LiteralExpr", + "value": 2 + }, + "type": { + "name": "int" + } + }, + { + "location": { + "from": "L43:16", + "to": "L43:17" + }, + "expr": { + "_type": "LiteralExpr", + "value": 3 + }, + "type": { + "name": "int" + } + }, + { + "location": { + "from": "L43:9", + "to": "L43:18" + }, + "expr": { + "_type": "ListExpr", + "items": [ + { + "_type": "LiteralExpr", + "value": 1 + }, + { + "_type": "LiteralExpr", + "value": 2 + }, + { + "_type": "LiteralExpr", + "value": 3 + } + ] + }, + "type": { + "name": "list", + "args": [ + { + "name": "int" + } + ], + "body": { + "name": "list" + } + } + }, + { + "location": { + "from": "L43:4", + "to": "L43:5" + }, + "expr": { + "_type": "VariableExpr", + "name": "i" + }, + "type": { + "name": "int" + } + }, + { + "location": { + "from": "L44:8", + "to": "L44:9" + }, + "expr": { + "_type": "VariableExpr", + "name": "i" + }, + "type": { + "name": "int" + } + }, + { + "location": { + "from": "L45:4", + "to": "L45:5" + }, + "expr": { + "_type": "VariableExpr", + "name": "i" + }, + "type": {} } ] } \ No newline at end of file