Skip to content

Commit 341d3cc

Browse files
authored
Consider non-empty enums assignable to Self (#19779)
Fixes #18345. Fixes #16558. See the linked ticket for reasoning - enums with members are implicitly final and should be treated exactly as if they were decorated with `@final`.
1 parent e35e05f commit 341d3cc

File tree

2 files changed

+33
-1
lines changed

2 files changed

+33
-1
lines changed

mypy/typeanal.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -779,7 +779,7 @@ def try_analyze_special_unbound_type(self, t: UnboundType, fullname: str) -> Typ
779779
if self.api.type.has_base("builtins.type"):
780780
self.fail("Self type cannot be used in a metaclass", t)
781781
if self.api.type.self_type is not None:
782-
if self.api.type.is_final:
782+
if self.api.type.is_final or self.api.type.is_enum and self.api.type.enum_members:
783783
return fill_typevars(self.api.type)
784784
return self.api.type.self_type.copy_modified(line=t.line, column=t.column)
785785
# TODO: verify this is unreachable and replace with an assert?

test-data/unit/check-selftype.test

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2346,3 +2346,35 @@ gc: G[D2]
23462346
reveal_type(gb.test()) # N: Revealed type is "typing.Sequence[__main__.D1]"
23472347
reveal_type(gc.test()) # N: Revealed type is "builtins.list[__main__.D2]"
23482348
[builtins fixtures/list.pyi]
2349+
2350+
[case testEnumImplicitlyFinalForSelfType]
2351+
from enum import Enum
2352+
from typing import Self
2353+
2354+
# This enum has members and so is implicitly final.
2355+
# Foo and Self are interchangeable within the class.
2356+
class Foo(Enum):
2357+
A = 1
2358+
2359+
@classmethod
2360+
def foo(cls) -> Self:
2361+
return Foo.A
2362+
2363+
@classmethod
2364+
def foo2(cls) -> Self:
2365+
return cls.bar()
2366+
2367+
@classmethod
2368+
def bar(cls) -> Foo:
2369+
...
2370+
2371+
# This enum is empty and should not be assignable to Self
2372+
class Bar(Enum):
2373+
@classmethod
2374+
def foo(cls) -> Self:
2375+
return cls.bar() # E: Incompatible return value type (got "Bar", expected "Self")
2376+
2377+
@classmethod
2378+
def bar(cls) -> Bar:
2379+
...
2380+
[builtins fixtures/classmethod.pyi]

0 commit comments

Comments
 (0)