From cf27233dc9da678db36674def2aa380060ed6f7c Mon Sep 17 00:00:00 2001 From: sobolevn Date: Mon, 4 Oct 2021 16:55:11 +0300 Subject: [PATCH 1/2] Adds check for unique enum keys --- mypy/semanal.py | 7 +++++++ test-data/unit/check-enum.test | 18 ++++++++++++++++++ 2 files changed, 25 insertions(+) diff --git a/mypy/semanal.py b/mypy/semanal.py index 49c24cde0447..5f2832ec4c76 100644 --- a/mypy/semanal.py +++ b/mypy/semanal.py @@ -2748,6 +2748,13 @@ def analyze_name_lvalue(self, existing = names.get(name) outer = self.is_global_or_nonlocal(name) + if not outer and isinstance(self.type, TypeInfo) and self.type.is_enum: + # Special case: we need to be sure that `Enum` keys are unique. + if self.type.names.get(name) is not None: + self.fail('Attempted to reuse key "{}" in Enum definition "{}"'.format( + name, self.type.name, + ), lvalue) + if (not existing or isinstance(existing.node, PlaceholderNode)) and not outer: # Define new variable. var = self.make_name_lvalue_var(lvalue, kind, not explicit_type) diff --git a/test-data/unit/check-enum.test b/test-data/unit/check-enum.test index 5200c00d3f28..aba76a1f0c08 100644 --- a/test-data/unit/check-enum.test +++ b/test-data/unit/check-enum.test @@ -1360,3 +1360,21 @@ class E(IntEnum): A = N(0) reveal_type(E.A.value) # N: Revealed type is "__main__.N" + + +[case testEnumReusedKeys] +# https://github.com/python/mypy/issues/11248 +from enum import Enum +class Foo(Enum): + A = 1 + A = 'a' # E: Attempted to reuse key "A" in Enum definition "Foo" \ + # E: Incompatible types in assignment (expression has type "str", variable has type "int") +reveal_type(Foo.A.value) # N: Revealed type is "builtins.int" + +class Bar(Enum): + A = 1 + B = A = 2 # E: Attempted to reuse key "A" in Enum definition "Bar" +class Baz(Enum): + A = 1 + B, A = (1, 2) # E: Attempted to reuse key "A" in Enum definition "Baz" +[builtins fixtures/tuple.pyi] From 148ec5e23baea895a48d85a1b955384cb380392e Mon Sep 17 00:00:00 2001 From: sobolevn Date: Mon, 4 Oct 2021 19:30:29 +0300 Subject: [PATCH 2/2] Addresses review --- mypy/semanal.py | 6 +++--- test-data/unit/check-enum.test | 19 ++++++++++++++++--- 2 files changed, 19 insertions(+), 6 deletions(-) diff --git a/mypy/semanal.py b/mypy/semanal.py index 5f2832ec4c76..e2d6abe7e7d3 100644 --- a/mypy/semanal.py +++ b/mypy/semanal.py @@ -2748,10 +2748,10 @@ def analyze_name_lvalue(self, existing = names.get(name) outer = self.is_global_or_nonlocal(name) - if not outer and isinstance(self.type, TypeInfo) and self.type.is_enum: + if kind == MDEF and isinstance(self.type, TypeInfo) and self.type.is_enum: # Special case: we need to be sure that `Enum` keys are unique. - if self.type.names.get(name) is not None: - self.fail('Attempted to reuse key "{}" in Enum definition "{}"'.format( + if existing: + self.fail('Attempted to reuse member name "{}" in Enum definition "{}"'.format( name, self.type.name, ), lvalue) diff --git a/test-data/unit/check-enum.test b/test-data/unit/check-enum.test index aba76a1f0c08..e49ad7d8fa0e 100644 --- a/test-data/unit/check-enum.test +++ b/test-data/unit/check-enum.test @@ -1365,16 +1365,29 @@ reveal_type(E.A.value) # N: Revealed type is "__main__.N" [case testEnumReusedKeys] # https://github.com/python/mypy/issues/11248 from enum import Enum +class Correct(Enum): + x = 'y' + y = 'x' class Foo(Enum): A = 1 - A = 'a' # E: Attempted to reuse key "A" in Enum definition "Foo" \ + A = 'a' # E: Attempted to reuse member name "A" in Enum definition "Foo" \ # E: Incompatible types in assignment (expression has type "str", variable has type "int") reveal_type(Foo.A.value) # N: Revealed type is "builtins.int" class Bar(Enum): A = 1 - B = A = 2 # E: Attempted to reuse key "A" in Enum definition "Bar" + B = A = 2 # E: Attempted to reuse member name "A" in Enum definition "Bar" class Baz(Enum): A = 1 - B, A = (1, 2) # E: Attempted to reuse key "A" in Enum definition "Baz" + B, A = (1, 2) # E: Attempted to reuse member name "A" in Enum definition "Baz" [builtins fixtures/tuple.pyi] + +[case testEnumReusedKeysOverlapWithLocalVar] +from enum import Enum +x = 1 +class Foo(Enum): + x = 2 + def method(self) -> None: + x = 3 +x = 4 +[builtins fixtures/bool.pyi]