diff --git a/mypy/literals.py b/mypy/literals.py index 0a836615734e..4779abf871c9 100644 --- a/mypy/literals.py +++ b/mypy/literals.py @@ -113,7 +113,10 @@ def visit_star_expr(self, e: StarExpr) -> Key: return ('Star', literal_hash(e.expr)) def visit_name_expr(self, e: NameExpr) -> Key: - return ('Var', e.name) + # N.B: We use the node itself as the key, and not the name, + # because using the name causes issues when there is shadowing + # (for example, in list comprehensions). + return ('Var', e.node) def visit_member_expr(self, e: MemberExpr) -> Key: return ('Member', literal_hash(e.expr), e.name) diff --git a/mypyc/test-data/run.test b/mypyc/test-data/run.test index 71a39fe6e80b..a00f06ec178e 100644 --- a/mypyc/test-data/run.test +++ b/mypyc/test-data/run.test @@ -4633,3 +4633,15 @@ with assertRaises(TypeError, "errored formatting real type!"): with assertRaises(TypeError, "tuple[int, native.A] object expected; got tuple[int, int]"): g((20, 30)) + +[case testComprehensionShadowBinder] +def foo(x: object) -> object: + if isinstance(x, list): + return tuple(x for x in x), x + return None + +[file driver.py] +from native import foo + +assert foo(None) == None +assert foo([1, 2, 3]) == ((1, 2, 3), [1, 2, 3]) diff --git a/test-data/unit/check-lists.test b/test-data/unit/check-lists.test index 1b75d59f6112..49b153555fd5 100644 --- a/test-data/unit/check-lists.test +++ b/test-data/unit/check-lists.test @@ -77,3 +77,11 @@ reveal_type(b) # N: Revealed type is 'builtins.list[builtins.int*]' c = [*a, 0] reveal_type(c) # N: Revealed type is 'builtins.list[builtins.int*]' [builtins fixtures/list.pyi] + +[case testComprehensionShadowBinder] +# flags: --strict-optional +def foo(x: object) -> None: + if isinstance(x, str): + [reveal_type(x) for x in [1, 2, 3]] # N: Revealed type is 'builtins.int*' + +[builtins fixtures/isinstancelist.pyi]