Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion mypy/typeanal.py
Original file line number Diff line number Diff line change
Expand Up @@ -779,7 +779,7 @@ def try_analyze_special_unbound_type(self, t: UnboundType, fullname: str) -> Typ
if self.api.type.has_base("builtins.type"):
self.fail("Self type cannot be used in a metaclass", t)
if self.api.type.self_type is not None:
if self.api.type.is_final:
if self.api.type.is_final or self.api.type.is_enum and self.api.type.enum_members:
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should enums have is_final set maybe, so they opt into all behaviors like this?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I was thinking about this. We can try this in a separate PR (this may also simplify a bit some of the mypyc code related to final enums).

return fill_typevars(self.api.type)
return self.api.type.self_type.copy_modified(line=t.line, column=t.column)
# TODO: verify this is unreachable and replace with an assert?
Expand Down
32 changes: 32 additions & 0 deletions test-data/unit/check-selftype.test
Original file line number Diff line number Diff line change
Expand Up @@ -2346,3 +2346,35 @@ gc: G[D2]
reveal_type(gb.test()) # N: Revealed type is "typing.Sequence[__main__.D1]"
reveal_type(gc.test()) # N: Revealed type is "builtins.list[__main__.D2]"
[builtins fixtures/list.pyi]

[case testEnumImplicitlyFinalForSelfType]
from enum import Enum
from typing import Self

# This enum has members and so is implicitly final.
# Foo and Self are interchangeable within the class.
class Foo(Enum):
A = 1

@classmethod
def foo(cls) -> Self:
return Foo.A

@classmethod
def foo2(cls) -> Self:
return cls.bar()

@classmethod
def bar(cls) -> Foo:
...

# This enum is empty and should not be assignable to Self
class Bar(Enum):
@classmethod
def foo(cls) -> Self:
return cls.bar() # E: Incompatible return value type (got "Bar", expected "Self")

@classmethod
def bar(cls) -> Bar:
...
[builtins fixtures/classmethod.pyi]
Loading