diff --git a/crates/ty_python_semantic/resources/mdtest/expression/boolean.md b/crates/ty_python_semantic/resources/mdtest/expression/boolean.md index ec78b20fb7..413acf8e39 100644 --- a/crates/ty_python_semantic/resources/mdtest/expression/boolean.md +++ b/crates/ty_python_semantic/resources/mdtest/expression/boolean.md @@ -78,6 +78,7 @@ python-version = "3.11" ``` ```py +import enum from typing import Literal, final reveal_type(bool(1)) # revealed: Literal[True] @@ -129,13 +130,20 @@ class FinalClassWithNoLenOrBool: ... reveal_type(bool(FinalClassWithNoLenOrBool())) # revealed: Literal[True] -def f(x: SingleElementTupleSubclass | FinalClassOverridingLenAndNotBool | FinalClassWithNoLenOrBool): +class EnumWithMembers(enum.Enum): + A = 1 + B = 2 + +reveal_type(bool(EnumWithMembers.A)) # revealed: Literal[True] + +def f(x: SingleElementTupleSubclass | FinalClassOverridingLenAndNotBool | FinalClassWithNoLenOrBool | Literal[EnumWithMembers.A]): reveal_type(bool(x)) # revealed: Literal[True] ``` ## Falsy values ```py +import enum from typing import final, Literal reveal_type(bool(0)) # revealed: Literal[False] @@ -156,13 +164,23 @@ class FinalClassOverridingLenAndNotBool: reveal_type(bool(FinalClassOverridingLenAndNotBool())) # revealed: Literal[False] -def f(x: EmptyTupleSubclass | FinalClassOverridingLenAndNotBool): +class EnumWithMembersOverridingBool(enum.Enum): + A = 1 + B = 2 + + def __bool__(self) -> Literal[False]: + return False + +reveal_type(bool(EnumWithMembersOverridingBool.A)) # revealed: Literal[False] + +def f(x: EmptyTupleSubclass | FinalClassOverridingLenAndNotBool | Literal[EnumWithMembersOverridingBool.A]): reveal_type(bool(x)) # revealed: Literal[False] ``` ## Ambiguous values ```py +import enum from typing import Literal reveal_type(bool([])) # revealed: bool @@ -182,6 +200,15 @@ class NonFinalOverridingLenAndNotBool: # because a subclass might override `__bool__`, # and `__bool__` takes precedence over `__len__` reveal_type(bool(NonFinalOverridingLenAndNotBool())) # revealed: bool + +class EnumWithMembersOverridingBool(enum.Enum): + A = 1 + B = 2 + + def __bool__(self) -> bool: + return False + +reveal_type(bool(EnumWithMembersOverridingBool.A)) # revealed: bool ``` ## `__bool__` returning `NoReturn` diff --git a/crates/ty_python_semantic/resources/mdtest/type_properties/truthiness.md b/crates/ty_python_semantic/resources/mdtest/type_properties/truthiness.md index 310b59602b..fc58c77482 100644 --- a/crates/ty_python_semantic/resources/mdtest/type_properties/truthiness.md +++ b/crates/ty_python_semantic/resources/mdtest/type_properties/truthiness.md @@ -183,13 +183,11 @@ class CustomLenEnum(Enum): def __len__(self): return 0 -# TODO: these could be `Literal[True]` -reveal_type(bool(NormalEnum.NO)) # revealed: bool -reveal_type(bool(NormalEnum.YES)) # revealed: bool +reveal_type(bool(NormalEnum.NO)) # revealed: Literal[True] +reveal_type(bool(NormalEnum.YES)) # revealed: Literal[True] -# TODO: these could be `Literal[False]` -reveal_type(bool(FalsyEnum.NO)) # revealed: bool -reveal_type(bool(FalsyEnum.YES)) # revealed: bool +reveal_type(bool(FalsyEnum.NO)) # revealed: Literal[False] +reveal_type(bool(FalsyEnum.YES)) # revealed: Literal[False] # All of the following must be `bool`: diff --git a/crates/ty_python_semantic/src/types.rs b/crates/ty_python_semantic/src/types.rs index 0ccb90b0cb..d64473ddb6 100644 --- a/crates/ty_python_semantic/src/types.rs +++ b/crates/ty_python_semantic/src/types.rs @@ -4692,10 +4692,10 @@ impl<'db> Type<'db> { Truthiness::Ambiguous } - Type::EnumLiteral(_) => { - // We currently make no attempt to infer the precise truthiness, but it's not impossible to do so. - // Note that custom `__bool__` or `__len__` methods on the class or superclasses affect the outcome. - Truthiness::Ambiguous + Type::EnumLiteral(enum_type) => { + enum_type + .enum_class_instance(db) + .try_bool_impl(db, allow_short_circuit, visitor)? } Type::IntLiteral(num) => Truthiness::from(*num != 0),