Repository: python/mypy

Issue:
Make enum values final
#10852 made it so that boolean values are correctly identified as Literals in an enum. Other values seem to not be:

```python a.py
from enum import Enum

class Truth(Enum):
    true = True
    false = False

class MyInt(Enum):
    a = 0
    b = 1

class MyStr(Enum):
    a = "a"
    b = "b"

reveal_type(Truth.false.value)
reveal_type(MyInt.a.value)
reveal_type(MyStr.a.value)

```

Results in:

```
a.py:15: note: Revealed type is "Literal[False]?"
a.py:16: note: Revealed type is "builtins.int"
a.py:17: note: Revealed type is "builtins.str"
```

Expected result:

```
a.py:15: note: Revealed type is "Literal[False]?"
a.py:16: note: Revealed type is "Literal[0]"
a.py:17: note: Revealed type is "Literal['a']"
```

The enum members themselves (MyInt.a) are correctly identified as final, but their values should be as well
Make enum values final
#10852 made it so that boolean values are correctly identified as Literals in an enum. Other values seem to not be:

```python a.py
from enum import Enum

class Truth(Enum):
    true = True
    false = False

class MyInt(Enum):
    a = 0
    b = 1

class MyStr(Enum):
    a = "a"
    b = "b"

reveal_type(Truth.false.value)
reveal_type(MyInt.a.value)
reveal_type(MyStr.a.value)

```

Results in:

```
a.py:15: note: Revealed type is "Literal[False]?"
a.py:16: note: Revealed type is "builtins.int"
a.py:17: note: Revealed type is "builtins.str"
```

Expected result:

```
a.py:15: note: Revealed type is "Literal[False]?"
a.py:16: note: Revealed type is "Literal[0]"
a.py:17: note: Revealed type is "Literal['a']"
```

The enum members themselves (MyInt.a) are correctly identified as final, but their values should be as well


Discussion:
Some experimentation shows that commenting out `lvalue.is_inferred_def = False` inside of `store_declared_types` in `semanal.py` fixes this issue, although I'm sure it breaks a bunch of other things. I don't yet understand enough to know why this is called for all types except bool
Some experimentation shows that commenting out `lvalue.is_inferred_def = False` inside of `store_declared_types` in `semanal.py` fixes this issue, although I'm sure it breaks a bunch of other things. I don't yet understand enough to know why this is called for all types except bool

Resolving patch:
diff --git a/mypy/semanal.py b/mypy/semanal.py
--- a/mypy/semanal.py
+++ b/mypy/semanal.py
@@ -2107,10 +2107,10 @@ def visit_assignment_stmt(self, s: AssignmentStmt) -> None:
         s.is_final_def = self.unwrap_final(s)
         self.analyze_lvalues(s)
         self.check_final_implicit_def(s)
+        self.store_final_status(s)
         self.check_classvar(s)
         self.process_type_annotation(s)
         self.apply_dynamic_class_hook(s)
-        self.store_final_status(s)
         if not s.type:
             self.process_module_assignment(s.lvalues, s.rvalue, s)
         self.process__all__(s)


Test patch:
diff --git a/test-data/unit/check-enum.test b/test-data/unit/check-enum.test
--- a/test-data/unit/check-enum.test
+++ b/test-data/unit/check-enum.test
@@ -618,8 +618,8 @@ reveal_type(A2.x.value)         # N: Revealed type is "builtins.int"
 reveal_type(A2.x._value_)       # N: Revealed type is "builtins.int"
 is_x(reveal_type(A3.x.name))    # N: Revealed type is "Literal['x']"
 is_x(reveal_type(A3.x._name_))  # N: Revealed type is "Literal['x']"
-reveal_type(A3.x.value)         # N: Revealed type is "builtins.int"
-reveal_type(A3.x._value_)       # N: Revealed type is "builtins.int"
+reveal_type(A3.x.value)         # N: Revealed type is "Literal[1]?"
+reveal_type(A3.x._value_)       # N: Revealed type is "Literal[1]?"
 
 B1 = IntEnum('B1', 'x')
 class B2(IntEnum):
@@ -639,8 +639,8 @@ reveal_type(B2.x.value)         # N: Revealed type is "builtins.int"
 reveal_type(B2.x._value_)       # N: Revealed type is "builtins.int"
 is_x(reveal_type(B3.x.name))    # N: Revealed type is "Literal['x']"
 is_x(reveal_type(B3.x._name_))  # N: Revealed type is "Literal['x']"
-reveal_type(B3.x.value)         # N: Revealed type is "builtins.int"
-reveal_type(B3.x._value_)       # N: Revealed type is "builtins.int"
+reveal_type(B3.x.value)         # N: Revealed type is "Literal[1]?"
+reveal_type(B3.x._value_)       # N: Revealed type is "Literal[1]?"
 
 # TODO: C1.x.value and C2.x.value should also be of type 'int'
 # This requires either a typeshed change or a plugin refinement
@@ -661,8 +661,8 @@ reveal_type(C2.x.value)         # N: Revealed type is "builtins.int"
 reveal_type(C2.x._value_)       # N: Revealed type is "builtins.int"
 is_x(reveal_type(C3.x.name))    # N: Revealed type is "Literal['x']"
 is_x(reveal_type(C3.x._name_))  # N: Revealed type is "Literal['x']"
-reveal_type(C3.x.value)         # N: Revealed type is "builtins.int"
-reveal_type(C3.x._value_)       # N: Revealed type is "builtins.int"
+reveal_type(C3.x.value)         # N: Revealed type is "Literal[1]?"
+reveal_type(C3.x._value_)       # N: Revealed type is "Literal[1]?"
 
 D1 = Flag('D1', 'x')
 class D2(Flag):
@@ -680,8 +680,8 @@ reveal_type(D2.x.value)         # N: Revealed type is "builtins.int"
 reveal_type(D2.x._value_)       # N: Revealed type is "builtins.int"
 is_x(reveal_type(D3.x.name))    # N: Revealed type is "Literal['x']"
 is_x(reveal_type(D3.x._name_))  # N: Revealed type is "Literal['x']"
-reveal_type(D3.x.value)         # N: Revealed type is "builtins.int"
-reveal_type(D3.x._value_)       # N: Revealed type is "builtins.int"
+reveal_type(D3.x.value)         # N: Revealed type is "Literal[1]?"
+reveal_type(D3.x._value_)       # N: Revealed type is "Literal[1]?"
 
 # TODO: Generalize our enum functional API logic to work with subclasses of Enum
 # See https://github.com/python/mypy/issues/6037
@@ -699,8 +699,8 @@ reveal_type(E2.x.value)         # N: Revealed type is "builtins.int"
 reveal_type(E2.x._value_)       # N: Revealed type is "builtins.int"
 is_x(reveal_type(E3.x.name))    # N: Revealed type is "Literal['x']"
 is_x(reveal_type(E3.x._name_))  # N: Revealed type is "Literal['x']"
-reveal_type(E3.x.value)         # N: Revealed type is "builtins.int"
-reveal_type(E3.x._value_)       # N: Revealed type is "builtins.int"
+reveal_type(E3.x.value)         # N: Revealed type is "Literal[1]?"
+reveal_type(E3.x._value_)       # N: Revealed type is "Literal[1]?"
 
 
 # TODO: Figure out if we can construct enums using EnumMetas using the functional API.
@@ -737,9 +737,9 @@ from enum import Enum
 class SomeEnum(Enum):
     a = "foo"
 [out]
-main:2: note: Revealed type is "builtins.int"
+main:2: note: Revealed type is "Literal[1]?"
 [out2]
-main:2: note: Revealed type is "builtins.str"
+main:2: note: Revealed type is "Literal['foo']?"
 
 [case testEnumReachabilityChecksBasic]
 from enum import Enum
@@ -1311,8 +1311,8 @@ class Foo(Enum):
     B = 2
 
 a = Foo.A
-reveal_type(a.value)    # N: Revealed type is "builtins.int"
-reveal_type(a._value_)  # N: Revealed type is "builtins.int"
+reveal_type(a.value)    # N: Revealed type is "Union[Literal[1]?, Literal[2]?]"
+reveal_type(a._value_)  # N: Revealed type is "Union[Literal[1]?, Literal[2]?]"
 
 [case testNewSetsUnexpectedValueType]
 from enum import Enum
@@ -1437,7 +1437,7 @@ class Foo(Enum):
     A = 1
     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"
+reveal_type(Foo.A.value)  # N: Revealed type is "Literal[1]?"
 
 class Bar(Enum):
     A = 1
@@ -1788,3 +1788,18 @@ class C(Enum):
 class D(C):  # E: Cannot inherit from final class "C"
     x: int   # E: Cannot assign to final name "x"
 [builtins fixtures/bool.pyi]
+
+[case testEnumLiteralValues]
+from enum import Enum
+
+class A(Enum):
+    str = "foo"
+    int = 1
+    bool = False
+    tuple = (1,)
+
+reveal_type(A.str.value)  # N: Revealed type is "Literal['foo']?"
+reveal_type(A.int.value)  # N: Revealed type is "Literal[1]?"
+reveal_type(A.bool.value)  # N: Revealed type is "Literal[False]?"
+reveal_type(A.tuple.value)  # N: Revealed type is "Tuple[Literal[1]?]"
+[builtins fixtures/tuple.pyi]
\ No newline at end of file
diff --git a/test-data/unit/merge.test b/test-data/unit/merge.test
--- a/test-data/unit/merge.test
+++ b/test-data/unit/merge.test
@@ -1435,7 +1435,7 @@ TypeInfo<0>(
   Bases(enum.Enum<1>)
   Mro(target.A<0>, enum.Enum<1>, builtins.object<2>)
   Names(
-    X<3> (builtins.int<4>))
+    X<3> (Literal[0]?<4>))
   MetaclassType(enum.EnumMeta<5>))
 ==>
 TypeInfo<0>(
@@ -1443,8 +1443,8 @@ TypeInfo<0>(
   Bases(enum.Enum<1>)
   Mro(target.A<0>, enum.Enum<1>, builtins.object<2>)
   Names(
-    X<3> (builtins.int<4>)
-    Y<6> (builtins.int<4>))
+    X<3> (Literal[0]?<4>)
+    Y<6> (Literal[1]?<4>))
   MetaclassType(enum.EnumMeta<5>))
 
 [case testLiteralMerge]